前言
Java 8是Java开发的一个主要版本,也是一个有着重大改变的版本,在此对Java 8的部分新特性进行总结,主要总结以下几个部分:
- Lambda表达式
- 函数式接口
- 默认方法
- Stream
Lambda表达式
Lambda表达式能够让我们把函数作为方法参数,或者把代码作为数据对待,使用Lambda表达式可以使代码变得更加简洁紧凑。语法如下:
- 方法体为表达式,该表达式的值作为返回值返回:
(parameters) -> expression
- 方法体为代码块,必须用
{}
包裹起来,且需要有一个return
返回值:(parameters) -> { statements; }
Lambda表达式还有以下几个特征:
- 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值
- 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号
Lambda表达式的简单例子:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 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)
Lambda表达式的原理:
- 在类编译时,会生成一个私有静态方法 + 一个内部类
- 在内部类中实现了函数式接口,在实现接口的方法中,会调用编译器生成的静态方法
- 在使用lambda表达式的地方,通过传递内部类实例,来调用函数式接口方法
函数式接口
函数式接口就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。函数式接口的重要特点是,我们能够使用Lambda实例化它们,如定义了一个函数式接口如下:1
2
3
4
5
interface GreetingService
{
void sayMessage(String message);
}
那么就可以使用Lambda表达式来表示该接口的一个实现(Java 8之前一般是用匿名类实现):1
GreetingService greetService1 = message -> System.out.println("Hello " + message);
注意该函数式接口上有一个新的注解@FunctionalInterface
,该接口不是必须的,用来标记该接口为只允许有一个抽象方法的函数式接口,当接口不符合函数式接口定义的时候,编译器会报错。
默认方法
接口的默认方法就是接口可以有实现方法,而且不需要实现类去实现其方法,我们只需要在方法名前面加个default
关键字即可实现默认方法。
为什么要有这个特性呢?首先,之前的接口是个双刃剑,好处是面向抽象而不是面向具体编程,缺陷是当需要修改接口的时候,需要修改全部实现了该接口的类,但是如果我们想给接口添加新方法的同时不影响已有的实现,就可以使用默认方法这个特性,解决接口的修改与现有的实现不兼容的问题。
如果一个类实现了多个接口,并且这些接口有相同的默认方法,此时可以覆盖接口的默认方法,也可以使用super
来调用指定接口的默认方法:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18public class DefaultMethodTest implements A, B {
public void print() {
A.super.print();
}
}
interface A{
default void print(){
System.out.println("a");
}
}
interface B{
default void print(){
System.out.println("b");
}
}
Java 8还可以在接口中提供静态方法的实现,例如:1
2
3
4
5interface C{
static void hello(){
System.out.println("hello");
}
}
Stream
流式操作分为中间操作和最终操作两种,最终操作返回一特定类型的结果,而中间操作返回流本身,这样就可以将多个操作依次串联起来。
生成流
在Java 8中,集合接口有两个方法来生成流:
stream()
:为集合创建串行流parallelStream()
:为集合创建并行流
forEach
Stream 提供了新的方法forEach
来迭代流中的每个数据。以下代码片段使用forEach
输出了10个随机数:1
2Random random = new Random();
random.ints().limit(10).forEach(System.out::println);
需要注意的是,forEach
是一个终止操作,也就是说该操作必须是流的最后一个操作,一旦被调用,Stream
就不能再使用了。
filter
filter
方法用于通过设置的条件过滤出元素。以下代码片段使用filter
方法过滤出空字符串:1
2List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl"); // 获取空字符串的数量
int count = strings.stream().filter(string -> string.isEmpty()).count();
limit
limit
方法用于获取指定数量的流。 以下代码片段使用limit
方法打印出10条数据:1
2Random random = new Random();
random.ints().limit(10).forEach(System.out::println);
sorted
sorted
方法用于对流进行排序。以下代码片段使用sorted
方法对输出的10个随机数进行排序:1
2Random random = new Random();
random.ints().limit(10).sorted().forEach(System.out::println);
并行程序
parallelStream
是流并行处理程序的代替方法。以下实例我们使用parallelStream
来输出空字符串的数量:1
2List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl"); // 获取空字符串的数量
int count = strings.parallelStream().filter(string -> string.isEmpty()).count();
我们可以很容易的在顺序运行和并行直接切换。