Java 8 (又称为 jdk 1.8) 是 Java 语言开发的一个主要版本。 Oracle 公司于 2014 年 3 月 18 日发布 Java 8 ;包含十多种新特性,大致分为以下几类:
(1)语言
(2)编译器
(3)库
(4)工具
(5)运行时(JVM)
主要新特性:
① Lambda 表达式 − Lambda允许把函数作为一个方法的参数(函数作为参数传递进方法中。
② 方法引用 − 方法引用提供了非常有用的语法,可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,方法引用可以使语言的构造更紧凑简洁,减少冗余代码。
③ 默认方法 − 默认方法就是一个在接口里面有了一个实现的方法。
④ Nashorn, JavaScr新工具 − 新的编译工具,如:Nashorn引擎 jjs、 类依赖分析器jdeps。
⑤ Stream API −新添加的Stream API(java.util.stream) 把真正的函数式编程风格引入到Java中。
⑥ Date Time API − 加强对日期与时间的处理。
⑦ Optional 类 − Optional 类已经成为 Java 8 类库的一部分,用来解决空指针异常。
⑧ ipt 引擎 − Java 8提供了一个新的Nashorn javascript引擎,它允许我们在JVM上运行特定的javascript应用。
1.Java语言的新特性
(1)Lambda表达式
1)是什么
Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。
Lambda是一个匿名函数。Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中,把代码当作数据处理)。
简单来说:可以看成是对匿名内部类的简写,使用Lambda表达式时,接口必须是函数式接口。
2)Lambda表达式包含三部分:
1、左边:指定了Lambda表达式需要的所有参数 ,参数列表
2、一个箭头符号:->
3、右边指定了Lambda方法体,即Lambda表达式要执行的功能,可以是表达式和代码块。
3)语法
<函数式接口> <变量名> = (参数1,参数2...) -> { //方法体 }
重要特征:
可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。返回值的类型也由编译器推理。
特点说明:
1、=右边的类型会根据左边的函数式接口类型自动推断;
2、如果形参列表为空,只需保留();
3、如果形参只有1个,()可以省略,只需要参数的名称即可;
4、如果执行语句只有1句,且无返回值,{}可以省略,若有返回值,则若想省去{},则必须同时省略return,且执行语句也保证只有1句;
5、形参列表的数据类型会自动推断;
6、lambda不会生成一个单独的内部类文件;
7、lambda表达式若访问了局部变量,则局部变量必须是final的,若是局部变量没有加final关键字,系统会自动添加,此后在修改该局部变量,会报错;
4)Lambda 表达式实例
// 1. 不需要参数,返回值为 5
() -> 5
// 2. 接收一个参数(数字类型),返回其2倍的值
x -> 2 * x
// 3. 接受2个参数(数字),并返回他们的差值
(x, y) -> x – y
// 4. 接收2个int型整数,返回他们的和
(int x, int y) -> x + y
// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)
(String s) -> System.out.print(s)
(2)函数式接口
函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。函数式接口可以被隐式转换为lambda表达式。
可以使用注解@FunctionalInterface标记该接口为函数式接口
(3)接口的默认方法和静态方法
static方法
1、使用static修饰接口中的方法并且必须有主体;
2、接口的static方法只能够被接口本身调用;接口名.方法名(…);
3、接口的static方法不能够被子接口继承;
4、接口的static方法不能够被实现类覆写及直接调用;
default方法
在接口中可定义一个使用default修饰有方法体的方法,接口中可以对这个方法提供默认的一种实现。
1、使用default修饰接口中的方法并且必须有主体;
2、接口的default方法不能够被接口本身调用,需要接口的实例(实现类对象)来调用;
3、接口的default方法可以背子接口继承、覆写或者直接调用;
4、接口的default方法可以被实现类覆写及直接调用;
(4)方法引用
1)构造方法引用
语法是Class::new
,或者更一般的形式:Class<T>::new
。
2)静态方法引用
语法是Class::static_method
。
3)成员方法引用
语法是Class::method
4)实例方法引用
语法是instance::method。
(5)重复注解
在Java 8中使用@Repeatable注解定义重复注解,实际上,这并不是语言层面的改进,而是编译器做的一个trick,底层的技术仍然相同。
反射API提供了一个新的方法:getAnnotationsByType(),可以返回某个类型的重复注解
2.Java编译器的新特性
2.1参数名称获取
在语言层面(使用反射API和Parameter.getName()方法)和字节码层面(使用新的javac编译器以及-parameters参数)提供支持。
3. Java官方库的新特性
Java 8增加了很多新的工具类(date/time类),并扩展了现存的工具类,以支持现代的并发编程、函数式编程等。
3.1Optional
Optional仅仅是一个容易:存放T类型的值或者null。它提供了一些有用的接口来避免显式的null检查。
3.2Streams
新增的Stream API(java.util.stream)将生成环境的函数式编程引入了Java库中。这是目前为止最大的一次对Java库的完善,以便开发者能够写出更加有效、更加简洁和紧凑的代码。
3.3Date/Time API(JSR 310)
4.Stream流式编程
4.1基本概念
JDK8 的Stream 不是 集合元素,也不是数据结构,它相当于一个 高级版本的 Iterator,不可以重复遍历里面的数据。有串行、并行两种执行模式。
特点:
Stream自己不会存储元素。
Stream的操作不会改变源对象。相反,他们会返回一个持有结果的新Stream。
Stream 操作是延迟执行的。它会等到需要结果的时候才执行。也就是执行终端操作的时候。
最终操作:返回一特定类型的结果。
中间操作:返回流本身。
4.2串行流和并行流:
串行流操作在一个线程中依次完成。并行流在多个线程中完成,主要利用了 JDK7 的 Fork/Join 框架来拆分任务和加速处理。相比串行流,并行流可以很大程度提高程序的效率。
4.3创建Stream
(1)由集合创建:
default Stream<E> stream()
: 返回一个顺序流。
default Stream<E> parallelStream()
: 返回一个并行流。
(2)由数组创建:
static <T> Stream<T> stream(T[] array)
: 返回一个流
重载形式,能够处理对应基本类型的数组:
(3)由值创建:
可以使用静态方法 Stream.of(), 通过显示值 创建一个流。它可以接收任意数量的参数。
可以使用静态方法 Stream.iterate() 和 Stream.generate()创建无限流。
注意:使用无限流一定要配合limit截断,不然会无限制创建下去。
4.4Stream的中间操作
Stream<T> distinct():
去重,通过流所生成元素的 hashCode() 和 equals() 去除重复元素。
Stream<T> filter(Predicate<? super T> predicate):
Predicate函数在上一篇当中我们已经讲过,它是断言型接口,所以filter方法中是接收一个和Predicate函数对应Lambda表达式,返回一个布尔值,从流中过滤某些元素。
Stream<T> sorted(Comparator<? super T> comparator):
Stream<T> limit(long maxSize):
Stream<T> skip(long n):
Stream<R> map(Function<? super T, ? extends R> mapper):
接收一个Function函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。也就是转换操作,map还有三个应用于具体类型方法,分别是:mapToInt,mapToLong和mapToDouble。这三个方法也比较好理解,比如mapToInt就是把原始Stream转换成一个新的Stream,这个新生成的Stream中的元素都是int类型。这三个方法可以免除自动装箱/拆箱的额外消耗。
Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper):
接收一个Function函数作为参数,将流中的每个值都转换成另一个流,然后把所有流连接成一个流。flatMap也有三个应用于具体类型的方法,分别是:flatMapToInt、flatMapToLong、flatMapToDouble,其作用于map的三个衍生方法相同。
4.5Stream的终端操作
终端操作执行中间操作链,并返回结果。终端操作我们就不一一介绍了,只介绍一下常用的操作。详细可看java.util.stream.Stream接口中的方法。
void forEach(Consumer<? super T> action):
内部迭代(需要用户去做迭代,称为外部迭代。相反,Stream API使用内部迭代帮你把迭代做了)
<R, A> R collect(Collector<? super T, A, R> collector):
收集、将流转换为其他形式,比如转换成List、Set、Map。collect方法是用Collector作为参数,Collector接口中方法的实现决定了如何对流执行收集操作(如收集到 List、Set、Map)。但是 Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例。例举一些常用的:
boolean allMatch(Predicate<? super T> predicate);
检查是否匹配所有元素。
boolean anyMatch(Predicate<? super T> predicate);
检查是否至少匹配一个元素。
boolean noneMatch(Predicate<? super T> predicate);
检查是否没有匹配所有元素。
Optional<T> findFirst();
返回当前流中的第一个元素。
Optional<T> findAny();
返回当前流中的任意元素。
long count();
返回流中元素总数。
Optional<T> max(Comparator<? super T> comparator);
返回流中最大值。
Optional<T> min(Comparator<? super T> comparator);
返回流中最小值。
T reduce(T identity, BinaryOperator<T> accumulator);
可以将流中元素反复结合起来,得到一个值。 返回 T。这是一个归约操作。
4.6Fork/Join框架
Fork/Join框架是java7中加入的一个并行任务框架,可以将任务拆分为多个小任务,每个小任务执行完的结果在合并成为一个结果。在任务的执行过程中使用工作窃取(work-stealing)算法,减少线程之间的竞争。