虽说 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 - 在 Java 8 之前如果我们要找到集合中第一个匹配元素,要使用外部循环,如下面方法 findFirstMatch() 如果找到一个大于 3 的数字立即返回它,否则返回 null
public Integer findFirstMatch() {
因为在 for 循环中找到第一个大于 3 的数字是 4, 并且立即返回,所以不管集合 integers 再大,也不会遍历整个集合。
List<Integer> integers = Arrays.asList(1, 4, 2, 5, 6, 3);
for(int i: integers) {
if(i > 3) return i;
}
return null;
}
注:不要纠结于上面示例方法的实际用途,实际上集体和匹配条件都该通过参数传入方法的,这里只作演示循环。
那么我们来到 Java 8 之后用 Stream API 该如何实现,翻遍了 Stream API, 能过滤元素的操作也就是 filter 方法,于是尝试这样的写法 Read More - 由于 Java 对集合的函数式操作并非原生态的,必须对得到的 stream() 进行过滤等操作,之后还是一个 stream(),一般我们最后返回给调用者需还原为相应的集合。这无法与 Scala 的
for ... yield操作相比。例如下面在使用 Stream API 过滤获得所有大于 3 的数字之后,方法的返回值还应该还原为 List<Integer>, 这个需求非常自然1List<Integer> list = Arrays.asList(1, 2, 3, 4, 5); 2List<Integer> greaterThan3(list) { 3 Stream<Integer> streamOfInteger = list.stream().filter( i -> i > 3); 4 return streamOfInteger.ToIntegerList......; 5}
我们这儿的问题就是如何把上面的 streamOfInteger 转换为 List<Integer>, 有以下几种办法 Read More - Java 在运用面向方向编程时,依照 AspectJ 的语法自己书写 *.aj 文件可以得到尽可能大的控制能力。如果是一个 sbt 的项目,有一个 sbt-aspectj-plugin 插件可以帮上我们的忙。那么如何应用这个插件呢? 该插件首页面告诉我们要在
project/plugins.sbt中加上下面这句话其他就是参考例子 runnable sample projects,然而这几个例子并非那么直白。所以还是自己做一个最简单的例子来体验 sbt 项目如何使用 AspectJ. Read More1addSbtPlugin("com.typesafe.sbt" % "sbt-aspectj" % "0.10.4")