java8 新特性
2016-05-23 21:09:01 最后更新: 2016-05-24 10:20:29 访问数量:506
2016-05-23 21:09:01 最后更新: 2016-05-24 10:20:29 访问数量:506
众所周知,java 有两个最具革命性的版本,一个是 java1.5 一个是 java1.8,上一篇日志中,我们介绍了 java5 也就是 java1.5 版本更新的十余个最具重要性的更新:
本篇日志中,我们就来介绍一下 java1.8 的新特性
Lambda 表达式的加入可以说是 java8 最大的变化,也是最受期待的 java 语法上的改变
随着近年来很多函数式语言的持续升温,尤其是 Scala 这种多范式语言的兴起,Scala 中常用的 Lambda 表达式也被推崇有加
Lambda 表达式是一个匿名函数,直接应用于代码中进行推算,可以用来表示一个闭包
所谓的“闭包”,指的就是可以包含自由变量的代码块,代码块中包含的自由变量并没有在定义是绑定任何对象,他们也不是在这个代码块内或任何全局上下文中定义的,而是在代码块环境中定义的局部变量,这个特性在很多种语言上都有着不同程度的支持
这里简单叙述的闭包概念看起来和传统意义上的函数非常类似,事实上这是完全不对的,所谓的闭包指的是一个代码块,十分类似于传统意义上的函数,但是这个局部的代码块必须是可以嵌套定义的,以及可以访问到他上级函数的局部变量
比如下面这个 C 语言中会报错的例子:
首先,C、C++ 中不支持嵌套定义函数,其次,函数中不能访问函数外的变量,因此上面这段闭包的代码是无法编译通过的
java 中 Lambda 表达式的形式十分简单,他由都好分割的参数列表、-> 符号与函数体三部分表示:
这里使用了 return 语句,但作为闭包的匿名函数却没有声明返回类型,编译器会自动根据 return 的参数设定这个匿名函数的返回类型
实际上,在循环遍历 ArrayList 的情况下,Lambda 表达式更加常用:
与返回类型一样,你也可以省去参数列表中的类型声明而让编译器自动判断,甚至如果你的函数体只有一行,你也可以省去大括号:
之前的例子也可以简化成
正如上面所说,闭包中可以引用闭包外的局部变量
Lambda 表达式的另一个支持是函数接口
“函数接口”这个概念是 java8 新引入的概念,他指的是只有一个显式声明的方法的接口,你可以使用 @FunctionalInterface 将只有一个显式方法的接口声明为一个函数接口,一旦添加这个注解,则意味着你的接口不能再放入另一个方法,当然,由于接口可以继承自其它接口,因此,他继承了父接口中的所有方法,这也就意味着,子接口与父接口中的方法数之和只能刚好是 1,然而,由于所有的接口都继承自 Object 类,所以接口中你也可以显式声明额外的 equals、hashCode、toString 方法
你可以用一个 Lambda 表达式为一个函数接口赋值:
上面已经介绍了 Lambda 表达式的基本语法,这里就不过多介绍各种参数及返回值的情况了
jdk1.8 中定义了三个常用的函数接口,十分实用,分别是有返回值、无返回值及返回 boolean 类型的函数接口:
现在,你可以十分方便的声明一个线程:
java8 对于类中的某个静态方法(包括构造器)或静态成员的方法,都可以使用方法引用的新语法来简化调用
下面我们通过一个 Lambda 表达式和一个方法引用的例子做对比来展示方法引用的用法:
上面例子中的 Lambda 表达式可以简化成:
由于 out 是 System 的静态成员变量,因此,可以直接通过 :: 符号引用他的成员方法
也可以直接引用参数的成员方法:
对于类静态方法,也可以直接使用 :: 符号来进行引用,与实例方法引用非常类似:
上面例子中的两个语句是完全一样的
Lambda 表达式中的参数可以直接作为构造器参数进行构造器引用
java8 中,接口可以声明默认的方法实现了,这被称为“默认方法”,这样的方法必须在前面加上 default 关键字
这让接口看起来更像是抽象类了,这与 java 诞生之初的设计初衷产生了矛盾,java 之所以不允许多重继承,以及接口诞生的一个原因正是因为 C++ 中多重继承引起的各种复杂的问题
这一次,java8 对 Collection、Comparator 等老接口添加了很多新方法,又不能强制修改所有这些老接口的实现,只能做出这样的妥协
随之而来,java 的某个类在实现多个接口的时候,又出现了新的问题,如果两个接口中都定义了完全一样的方法,同时有都为他们各自的这个方法添加了默认实现,那么实现类中的又不对相应方法采取自定义的实现,那么显然,这就会发生冲突,如果实现类中需要使用某个父接口的默认实现,就要使用下面的语法了:
java8 中,接口也因此可以有静态方法的实现,静态接口的默认实现无需添加 default 关键字:
java8 中,几乎可以为任何东西添加注解:局部变量、泛型类、父类与接口的实现,就连方法的异常也能添加注解
java8 中,通过反射机制,可以获取到参数名了,而在此之前,由于参数名不会保留在 java 字节码中,所以在运行时是无法取得的
想要利用这个新特性,就需要在编译中添加 -parameters 参数,可以在 maven 中配置:
运行下面的代码:
如果没有配置 -parameters 参数,则会输出:
Parameter: arg0
如果配置了 -parameters 参数,则会输出:
Parameter: args
此外,Parameter类有一个很方便的方法isNamePresent()来验证是否可以获取参数的名字
java8 增加了 Optional 类,他是一个简单的容器,包含一个泛型元素或 null,用来防止抛出 NullPointerException 异常,这是受到 Google Guava 类库启发而诞生的新特性
下面的代码展示了他的用法:
输出了:
Full Name is set? false
Full Name: [none]
Hey Stranger!
在数据库查询等方面具有非常实用的价值
Stream 类库(java.util.stream)的添加,将真正的函数式编程风格引入到了 java 中,让代码变得极其简洁
下面的例子展示了如何将一个 ArrayList 中的所有元素的 id 组合到一个 Set 中:
更加复杂的,比如我们要计算一个 task 的 List 中所有状态为 OPEN 的任务积分之和,我们调用每个 task 的 getPoints 方法获取积分,使用 Stream 的方式,你可以看到下面的代码:
输出了:
Total points: 18
接下来,我们计算整个集合中每个 task 的分数平均值:
输出了:
[19%, 50%, 30%]
Stream 可以原生实现并发:
输出了:
Total points (all tasks): 26.0
这个例子和上面的例子很像,但他是用并发的方式实现的,使用 reduce 方法收集结果
stream 结合 Collectors 的 groupingBy 方法可以实现分组功能:
输出了:
{CLOSED=[[CLOSED, 8]], OPEN=[[OPEN, 5], [OPEN, 13]]}
java8 中首次将 Base64 收入标准 java 类库中,实现了非常简单的 Base64 编解码的使用方法:
输出了:
QmFzZTY0IGZpbmFsbHkgaW4gSmF2YSA4IQ==
Base64 finally in Java 8!
java 对日期时间的处理一直以来饱受诟病,也因此催生了 Joda-Time 项目来简化日期时间的处理,java8 汲取了 Joda-Time 的精髓,诞生了 java.time 包
在并行与并发方面,java8 做了很大的改进,如我们前面提到的在 Stream、Lambda 表达式的基础上,可以轻松实现并行处理
同时,java 为 java.util.Arrays 提供了多个 parallelXXX 方法,可是实现自动的并行执行,如排序、设置值等,java.util.concurrent.ConcurrentHashMap 也加入了很多用于并发处理的聚集操作方法
java8 在 java.util.concurrent.ForkJoinPool 类中加入了一些新方法用来支持共有资源池(common pool)
新增的java.util.concurrent.locks.StampedLock类提供一直基于容量的锁,这种锁有三个模型来控制读写操作
在java.util.concurrent.atomic包中还增加了下面这些类:
后面,博主会有专门介绍 java 并发机制及使用的详细介绍博文,敬请期待
java8 提供了一个新的工具,用来分析一个 class 文件、目录或 jar 包的依赖
如执行:
会输出:
org.springframework.core-3.0.5.RELEASE.jar -> C:\Program Files\Java\jdk1.8.0\jre\lib\rt.jar
org.springframework.core (org.springframework.core-3.0.5.RELEASE.jar)
-> java.io
-> java.lang
-> java.lang.annotation
-> java.lang.ref
-> java.lang.reflect
-> java.util
-> java.util.concurrent
-> org.apache.commons.logging not found
-> org.springframework.asm not found
-> org.springframework.asm.commons not found
org.springframework.core.annotation (org.springframework.core-3.0.5.RELEASE.jar)
-> java.lang
-> java.lang.annotation
-> java.lang.reflect
-> java.util
在 java7 移除永久代 String 对象的常量池以后,java8 中将永久代 PermGen 空间彻底移除,取而代之的是元空间 Metaspace,它存储 java 类元数据,通过 -XX:MetaSpaceSize 与 -XX:MaxMetaspaceSize 参数设置他的大小
如果依然设置了 -XX:PermSize 与 -XX:MaxPermSize 选项,java 并不会报错,但是会忽略这两个选项的值
String 提供了 join 方法:
实现了类似于 php 中的 implode 方法,将一组字符串连接为一个分隔符分隔的字符串
输出了:
hello,world