Java 8 (又称为 jdk 1.8) 是 Java 自 Java 5(发布于2004年)之后的最重要的版本,这个版本在语言、编译器、库、工具和 JVM 等方面有很多新特性。
一、Lambda
Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中),或者把代码本身当作数据处理。
使用 Lambda 表达式可以使代码变的更加简洁紧凑。
(1)以下是 lambda 表达式的重要特征:
1)可选类型声明:
不需要声明参数类型,编译器可以统一识别参数值
。2)可选的参数圆括号:一个参数无需定义圆括号,但零个或多个参数需要定义圆括号。
3)可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
4)可选的返回关键字:
如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指明表达式返回了一个数值
。
(2)使用 Lambda 表达式需要注意以下两点:
Lambda 表达式主要用来定义行内执行的方法类型接口,例如一个简单方法接口。
Lambda 表达式免去了使用匿名方法的麻烦,并且给予 Java 简单但强大的函数化的编程能力。
(3)变量作用域
1)lambda 表达式只能引用标记了 final 的外层局部变量,这就是说不能在lambda 内部修改定义在域外的局部变量,否则会编译错误。
final static String salutation = "hello "; public static void main(String[] args) { GreetingService greetingService = message -> System.out.println(salutation + message); greetingService.sayMessage("Runoob"); } interface GreetingService { void sayMessage(String message); }
2)我们也可以直接在 lambda 表达式中访问外层的局部变量:
public static void main(String[] args) { final int num = 1; Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num)); s.convert(2); // 输出结果为 3 } interface Converter<T1, T2> { void convert(int i); }
3)lambda 表达式的局部变量可以不用声明为 final,但是必须不可被后面的代码修改(即隐性的具有final 的语义)
int num = 1; Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num)); s.convert(2); // 输出结果为 3 num = 5; // Error:(58, 93) java: 从lambda 表达式引用的本地变量必须是最终变量或实际上的最终变量
二、Java 8 方法引用
方法引用使得开发者可以直接引用现存的方法、Java类的构造方法或者实例对象。方法引用和Lambda表达式配合使用,使得java类的构造方法看起来紧凑而简洁,没有很多复杂的模板代码。方法引用使用一对冒号“ :: ”。
public static class Car {
public static Car create( final Supplier< Car > supplier ) {
return supplier.get();
}
public static void collide( final Car car ) {
System.out.println( "Collided " + car.toString() );
}
public void follow( final Car another ) {
System.out.println( "Following the " + another.toString() );
}
public void repair() {
System.out.println( "Repaired " + this.toString() );
}
}
(1)第一种方法引用的类型是构造器引用,语法是Class::new,或者更一般的形式:Class::new。注意:这个构造器没有参数。
final Car car = Car.create( Car::new );
final List< Car > cars = Arrays.asList( car );
(2)第二种方法引用的类型是静态方法引用,语法是Class::static_method。注意:这个方法接受一个Car类型的参数。
cars.forEach( Car::collide );
(3)第三种方法引用的类型是某个类的成员方法的引用,语法是Class::method,注意,这个方法没有定义入参:
cars.forEach( Car::repair );
(4)第四种方法引用的类型是某个实例对象的成员方法的引用,语法是instance::method。注意:这个方法接受一个Car类型的参数:
final Car police = Car.create( Car::new );
cars.forEach( police::follow );
运行上述例子,可以在控制台看到如下输出(Car实例可能不同):
Collided com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d
Repaired com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d
Following the com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d
三、Java 8 函数式接口
四、stream
五、Java 8 默认方法
Java 8 新增了接口的默认方法。简单说,默认方法就是接口可以有实现方法,而且不需要实现类去实现其方法
。我们只需在方法名前面加个 default 关键字即可实现默认方法
。
接口是个双刃剑,好处是面向抽象而不是面向具体编程,缺陷是当需要修改接口时候,需要修改全部实现该接口的类。所以引进默认方法,目的是为了
解决接口的修改与现有的实现不兼容的问题
。
六、Java 8 日期时间 API
Java 8 通过发布新的 Date-Time API (JSR 310)来进一步加强对日期与时间的处理。
在旧版的 Java 中,日期时间API 存在诸多问题,其中有:
1)非线程安全 − java.util.Date 是非线程安全的,所有的日期类都是可变的,这是 Java 日期类最大的问题之一; 2)设计很差 − Java 的日期/时间类的定义并不一致,在 java.util 和 java.sql 的包中都有日期类,此外用于格式化和解析的类在 java.text 包中定义。java.util.Date 同时包含日期和时间,而 java.sql.Date 仅包含日期,将其纳入 java.sql 包并不合理。另外这两个类都有相同的名字,这本身就是一个非常糟糕的设计。
3)时区处理麻烦 − 日期类并不提供国际化,没有时区支持,因此 Java 引入了 java.util.Calendar 和 java.util.TimeZone 类,但他们同样存在上述所有的问题。
Java 8 在 java.time 包下提供了很多新的 API。以下为两个比较重要的 API:
1) Local(本地) − 简化了日期时间的处理,没有时区的问题。
2)Zoned(时区) − 通过制定的时区处理日期时间。
新的 java.time 包涵盖了所有处理日期,时间,日期/时间,时区,时刻(instants),过程(during)与时钟(clock)的操作。
七、Nashorn JavaScript 引擎
Java 8 提供了新的 Nashorn JavaScript 引擎,使得我们可以在 JVM 上开发和运行 JS 应用。Nashorn JavaScript 引擎是 javax.script.ScriptEngine 的另一个实现版本,这类 Script 引擎遵循相同的规则,允许 Java 和 JavaScript 交互使用