- Java 在定义字符串的时候不支持字符串插值, 即不能在字符串中捕获作用域中的变量, 用来组成当前字符串. 而这可以说是其他各种言都具备的基本特性. 例如 Bash 中可以这样
bash-3.2$ name=Yanbin
Java 中不支持类似的方式
bash-3.2$ echo "Hello $name"
Hello YanbinString name = "Yanbin";
而使用 Java 必须用 String 的静态方法 format (注意是静态方法哦) 来间接的格式化出一个字符串, 可以这么写
String greeting = "Hello $name"; //Java 中无法把 $name 替换为 name 变量值 "Yanbin"String.format("Hello %s", "World");
我们看到在用 format 格式化时匹配参数会是一个不小的问题, 相同使用法的打印输出方法是
String.format("%1$s %1$s %s %s %2$s", "aa", 10); //aa aa aa 10 10System.out.printf(). 别说字符串插值了, 如果 format 方法是 String 的一个实例方法应用起来都会便利些, 如"Hello %s".format("World")会容易读些, Scala 的字符串实例就有format方法了. Read More - 希望 Java 能支持动态对象(匿名对象) 的特性是源于想要 Java 方法能优雅的返回多个值. 目前如果希望 Java 方法返回多个值的做法有返回一个自定义对象, 数组或列表, 或 Map. 这种需求多发生在私有方法上, 但目前的解决办法有如下弊端:
- 如果用自定义类来作为返回类型的话, 会使得类过于杂乱, 而且这些自定义类的复用性不高
- 数组或列表有太强的顺序依赖, 没有属性名告知每个位置上值的意义, 而且类型都必须为 Object
- 返回 Map 的, 在维护 Key 上容易出错(或须为 Key 定义很多字符串常量), 且类型也是为 Object.
说了上面许多, 各位可能还不定清楚我想要的是什么, 其实就是类似于 C# 的匿名对象(或曰动态对象), 看一段 C# 的方法返回一个匿名对象的例子(.NET3.5 开始引入匿名对象, .NET 4.0 后匿名对象可以作为函数值) Read More - 曾几何时在业务分层结构中的 VO 或 DTO 层充斥着无数的标准 JavaBean 类, 那些碍手脚的 getter/setter 方法简值不忍直视. 或许 JavaBean 设定规范的用意是当某些属性为只读时不提供 setter 方法, 而实际使用时, 因 getter/setter 都同时具备, 那么 JavaBean 的所有私有属性又何异于公有属性呢.
更别说对于某些形式的属性名, 若属性名为xCoordinate时, 它所对应的 getter 方法分别是getxCoordinate(),一般的 IDE 都会为它自动生成getXCoordinate()方法, 这是错误的. 实际上getXCoordinate()对应的属性名是XCoordinate.
所以 Play Framework 1 以及 Play Framework 2.4.6 之前的版本采用了字节码增强的技术, 实现了像 Objective-C 的 @property 的特性, 即只要声明公有属性, 编译器为该属性生成默认的 getter/setter 方法, 您也可以手工去覆盖个别默认的 getter/setter 方法.
因此在 Play Framework 中书写的的 model 类就只需要属性了, 像1public class User { 2 public int id; 3 public String name; 4 public String email; 5 public String address; 6}
就这么简单, 想像一下如果我们为一个众多属性的 model 类补全所有的 getter/setter 方法读起来有多恐怖. Read More 虽说 Java 的支持泛型以及近期 Lambda 表达式的加入, 在对类型进行推断上已经很强大了, 但在类型声明的时候仍然略显冗余, 最主要的一点是 Java 不能像 Scala 那样在声明变量有赋值的情况下进行类型推断. 我们先来看下 Java 已经为我们所进行的改进:
1List<String> strings = new ArrayList<String>(); //刚引入泛型时我们是这样 2List<String> strings = new ArrayList<>(); //后来变成这样了, 可以钻石符号推断, Java 7 3 4List<String> list = new ArrayList<>(); 5list.addAll(new ArrayList<>()); //根据 list.addAll() 上下文推断要创建的类型是 new ArrayList<String>(), Java 8 6 7interface Foo { 8 void dodo(); 9} 10Foo foo = () -> System.out.println("Foo:dodo()"); //Java 8 Lambda 可以根据 Lambda 表达式的签名推断出接口类型 11foo.dodo();在 Java 7 的泛型方法其实也是可以通过声明类型与方法参数来推断要返回的具体类型的. Read More
- 对于面向对象的语言不知道除了 Java 还有没别的语言会拿怎么比较两个字符串相等频频作为面试题来考. 原本是在编程语言中两个字符串内容是否相等时用
==比较时却可能是不对的. 在 Java 中1"ab" == "ab" //true 2"ab" == "new String("ab") //false 3"ab" == String.value("ab") //true 4new String("ab").equals(new String("ab")) //true 5new String("ab").intern() == new String("ab").intern() //true
在 Java 中明明看到两个字符串内容一样用==进行比较多数时候不是你想要的结果, 只有用equals()方法才是王道. 使用 Java 的字符串必须了解它内部是怎么存储的. 比于上面的结果我不作细说, 主要涉及到字符串常量池及内部状态,==比较引用,equals()比较内容.
Java 还常常对equals比较字符串津津乐道, 而我仍然认为它是语言设计上的一个缺陷, 所以 JVM 上的其他编程语言如 Groovy, Scala 纷纷倒勾, 无一不是用==来比较字符串的内容, 它们也提供字符串引用的比较, 但多少人实际关心两个字符串的引用是否相同呢, 反正字符串设计的是 Immutable 的.
若说是因为 Java 不支持操作符的重载, 但可以像 Scala, Groovy 那样在编译器上下功夫的. 最终我想依然是受累于 100% 源代码与二进制的兼容性, 改进的话会造成早先代码的行为错乱. Read More - Java 语言由于一直要保持源代码及二进制的向后兼容, 所以尽管语法上有了很大的演进, 但有些东西仍然无法触及. 不像有些新生代的语言设计时可以博采众长, 或者像 Scala 那种语言向后兼容性的要求没这么苛刻, Scala 只要保持主, 次版本相同时的兼容性. Java 则不同, 1.0 的代码或字节码放到 1.8 下还要能跑.
实际应用中在多个语言切换时, 很自然的会对其他语言与 Java 进行横向对比. 感觉 Java 语言有几个缺陷, 不在此篇中一一列举, 只把第一个不足之处道来.
Java 没有 Here Document 的支持, Here Document 又称 heredoc, hereis, here-string, here-script, 再通俗点讲就是多行字符串(multiline string). 在 Java 中如果要定义多行字符串, 需要连串的加号及换行符(\n), 如下1String content = "This is a\n" 2 + "multiline\n" 3 + "string content"
曾经为了寻求 Java 对 Here Document 的支持, 尝试过 Java 的 APT(Java Annotation Processing Tool), 定义注解 @HereDocument 使用注释, 编译时带-processor参数, 但是注释中的格式很容易被 IDE 给自动格式化掉, 见 Java 的多行字符串 Here Document 的实现. 所以这种尝试很快就放弃了, 因为项目中用 Java 和 Scala 混合编程, 真正需要 Here Document 的地方直接用 Scala 代码就行了. Read More 现在的 JMockit 已经偷偷升级到了 1.23 版了,在 JVM 上的 Mock 工具中就数它最无敌了,因为它抢夺了最佳控制点 --javaagent, 可以说它是无所不能的。一般我们使用 JMockit 是通过两种方式,
new MockUp和new Expectations. JMock 不仅能够 Mock 类的所有方法,还能部分 Mock -- 这个是new Expectations的默认行为。 所以这里我们来看下在使用new Expectations的情况下如何对类的部份静态方法或部分实例方法进行 Mock。大致表述一下,共分为三种情况
- 针对类进行 Mock, 只有录制的静态方法被 Mock 住,其他的静态方法或实例方法都会调用实际实现
- 针对某一实例进行 Mock,只在调用该实例已录制的方法才被 Mock 住,静态方法或新建实例调用任何方法都是实际实现
- 针对类进行 Mock,但录制的是一个实例方法,那么该实例或任何新建实例在调用该录制方法时都会被 Mock 住
不知道上面在说什么,本来就是空洞无凭,所以还是下实例,假定要测试下面这个类,或者说是测试使用到下面类的其他类 Read More
PlayFramework 已全面向 Google Guice 演进, 由于我们的项目是从 Play 2.0 开始搭建, 现虽已升级到了 2.4.6, 但对 Guice 的还没抱得那么的紧, 有点积重而慢步前挪. Google Guice 也是一个依赖注入(DI) 容器, 据说它比 Spring 快了很多很多, 是轻量级项目或框架首选, Google 出品俱是精品.
既然它是轻量级的 DI, 那么必须呈上一个最轻量级的入门, 官方的 Get Started 文档其实啰嗦了点, 对于 BillingService 依赖的 CreditCardProcess, TransactionLog, 和 Receipt 都未有交待, 算不上一个完整项目. 所以我这里尽量简化, 又不偏离一个最简单的项目模型. 那就是下面那个例子, 它实现了在不同时间发出不同的问候.
项目需要依赖 Guice 及它的子依赖, 这得看你是用什么来管理依赖的, 依照 http://mvnrepository.com/artifact/com.google.inject/guice/4.0 根据实际情况加上依赖配置, 前面这个链接默认为 sbt 的, 我还真偏爱了 sbt, 若是 Maven 就配置 Maven 的 pom.xml 吧. Guice 的引入带上以下几个依赖包
+-com.google.inject:guice:4.0
+-aopalliance:aopalliance:1.0
+-com.google.guava:guava:16.0.1
+-javax.inject:javax.inject:1Guice 目前是 4.0. Read More
- Java 7 首次引入了 fork/join 框架,但一直未曾直接尝试. 而且基本上也很少在实际项目中直接写 fork-join 的代码,在我们使用第三方组件时倒是间接会接触到 fork/join 框架。譬如 Akka 的 fork-join-executor, sbt 执行测试用例时也是默认 fork/join 并发执行。fork-join 可以帮助我们把计算任务粒度细化,并更有效的利用多 CPU 内核。
fork-join 与 map-reduce 有些相妨,在 Java 7 时代我其实是忽视了它的存在。目今正在了解 Java 8 的 parallelStream 时,因为它的底层实现也是 fork/join, 所以有兴致去稍加体验一下。fork/join 的算法简单来讲就是递归对半去细化计算任务,及到不能细化时由多内核(线程)去计算被拆分的任务,最后反方向把结果汇总。
下面是从 《Java 8 IN ACTION》中截的一个说明 fork/join 的处理过程 Read More - 前面一篇 Java 8 的 groupingBy 能否产生空的 Map 分组 是提出来的思考,本篇就是上一篇的答案。
由于在 Java 8 中用 Collectors.groupingBy 对 List 进行分组时每个组里都必须存在元素,也就是Stream<Person> stream = Stream.of(new Person("Tom", "male"), new Person("Jerry", "male"));
只能得到结果
System.out.println(stream.collect(Collectors.groupingBy(person -> person.gender)));{male=[Tom, Jerry]}
而无法表示存在其他 gender 的可能性,并且 female=[] 的情况,即想要结果{male=[Tom, Jerry], female=[]}
如果想得到以上的结果该当如何呢? stream.collect() 接受一个 Collector, Collectors 中只是定义了许多常用的 Collector 实现,如果不够用的话我们可以实现自己的 Collector. 下面就来定义一个 GroupingWithKeys, 它需要实现 java.util.stream.Collector 接口,有五个接口方法. 事成之后我们写 Read More