jdk8新特性(实例以及补充)
一、Java 8 Lambda 表达式
Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。
Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。
使用Lambda 表达式可以使代码变的更加简洁紧凑。
1.1 语法
lambda 表达式的语法格式如下:
(parameters) -> expression或(parameters) ->{statements; }
以下是lambda表达式的重要特征:
· 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
· 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
· 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
· 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。
1.2 Lambda 表达式实例
在Java8Tester.java 文件输入以下代码:
Java8Tester.java文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
public class Java8Tester { public static void main(String args[]) { Java8Tester tester = new Java8Tester(); // 类型声明 MathOperation addition = ( int a, int b) -> a + b; // 不用类型声明 MathOperation subtraction = (a, b) -> a - b; // 大括号中的返回语句 MathOperation multiplication = ( int a, int b) -> { return a * b; }; // 没有大括号及返回语句 MathOperation division = ( int a, int b) -> a / b; System.out.println( "10 + 5 = " + tester.operate( 10 , 5 , addition)); System.out.println( "10 - 5 = " + tester.operate( 10 , 5 , subtraction)); System.out.println( "10 x 5 = " + tester.operate( 10 , 5 , multiplication)); System.out.println( "10 / 5 = " + tester.operate( 10 , 5 , division)); // 不用括号 GreetingService greetService1 = message -> System.out.println( "Hello " + message); // 用括号 GreetingService greetService2 = (message) -> System.out.println( "Hello " + message); greetService1.sayMessage( "Runoob" ); greetService2.sayMessage( "Google" ); } interface MathOperation { int operation( int a, int b); } interface GreetingService { void sayMessage(String message); } private int operate( int a, int b, MathOperation mathOperation) { return mathOperation.operation(a, b); } } |
执行以上脚本,输出结果为:
10+5=15
10-5=5
10 x 5=50
10/5=2
HelloRunoob
HelloGoogle
使用Lambda 表达式需要注意以下两点:
· Lambda 表达式主要用来定义行内执行的方法类型接口,例如,一个简单方法接口。在上面例子中,我们使用各种类型的Lambda表达式来定义MathOperation接口的方法。然后我们定义了sayMessage的执行。
· Lambda 表达式免去了使用匿名方法的麻烦,并且给予Java简单但是强大的函数化的编程能力。
1.3 变量作用域
lambda 表达式只能引用标记了 final 的外层局部变量,这就是说不能在lambda 内部修改定义在域外的局部变量,否则会编译错误。
在Java8Tester.java 文件输入以下代码:
Java8Tester.java文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
public class Java8Tester { final static String salutation = "Hello! " ; public static void main(String args[]){ GreetingService greetService1 = message -> System.out.println(salutation + message); greetService1.sayMessage( "Runoob" ); //====================相当于下面============================== GreetingService g = new GreetingService() { @Override public void sayMessage(String message) { System.out.println(salutation + message); } }; g.sayMessage( "jack" ); |
1
2
3
4
5
|
//=========================================================== } interface GreetingService { void sayMessage(String message); } |
1
|
} |
执行以上脚本,输出结果为:
Hello! Runoob
Hello! jack
我们也可以直接在lambda 表达式中访问外层的局部变量:
Java8Tester.java文件
1
2
3
4
5
6
7
8
9
10
11
|
public class Java8Tester { 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 } public interface Converter<T1, T2> { void convert( int i); } } |
lambda 表达式的局部变量可以不用声明为 final,但是必须不可被后面的代码修改(即隐性的具有final 的语义)
1
2
3
4
5
6
7
8
9
10
11
|
public class Java8Tester { public static void main(String args[]) { int num = 1 ; Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num)); s.convert( 2 ); num = 5 ; } public interface Converter<T1, T2> { void convert( int i); } } |
1
|
//报错信息:Local variable num defined in an enclosing scope must be final or effectively final |
1
|
把num= 5 ;注释掉就不报错了 |
在Lambda 表达式当中不允许声明一个与局部变量同名的参数或者局部变量。
把String first = "";注掉就不报错了
二、Java 8 方法引用
方法引用通过方法的名字来指向一个方法。
方法引用可以使语言的构造更紧凑简洁,减少冗余代码。
方法引用使用一对冒号 :: 。
2.1方法引用
下面,我们在 Car 类中定义了 4 个方法作为例子来区分 Java 中 4 种不同方法的引用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
class Car { @FunctionalInterface public interface Supplier<T> { T get(); } //Supplier是jdk1.8的接口,这里和lamda一起使用了 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()); } public static void main(String[] args) { //构造器引用:它的语法是Class::new,或者更一般的Class< T >::new实例如下: Car car = Car.create(Car:: new ); Car car1 = Car.create(Car:: new ); Car car2 = Car.create(Car:: new ); Car car3 = new Car(); List<Car> cars = Arrays.asList(car,car1,car2,car3); System.out.println( "===================构造器引用========================" ); //静态方法引用:它的语法是Class::static_method,实例如下: cars.forEach(Car::collide); System.out.println( "===================静态方法引用========================" ); //特定类的任意对象的方法引用:它的语法是Class::method实例如下: cars.forEach(Car::repair); System.out.println( "==============特定类的任意对象的方法引用================" ); //特定对象的方法引用:它的语法是instance::method实例如下: final Car police = Car.create(Car:: new ); cars.forEach(police::follow); System.out.println( "===================特定对象的方法引用===================" ); } } |
2.2方法引用实例
在Java8Tester.java 文件输入以下代码:
Java8Tester.java文件
1
2
3
4
5
6
7
8
9
10
11
|
public class Java8Tester { public static void main(String args[]) { List names = new ArrayList(); names.add( "Google" ); names.add( "Runoob" ); names.add( "Taobao" ); names.add( "Baidu" ); names.add( "Sina" ); names.forEach(System.out::println); } } |
实例中我们将System.out::println 方法作为静态方法来引用。
执行以上脚本,输出结果为:
1
2
3
4
5
|
Google Runoob Taobao Baidu Sina |
三、Java 8 函数式接口
函数式接口(FunctionalInterface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。
函数式接口可以被隐式转换为lambda表达式。
函数式接口可以现有的函数友好地支持 lambda。
JDK 1.8之前已有的函数式接口:
· java.lang.Runnable
· java.util.concurrent.Callable
· java.security.PrivilegedAction
· java.util.Comparator
· java.io.FileFilter
· java.nio.file.PathMatcher
· java.lang.reflect.InvocationHandler
· java.beans.PropertyChangeListener
· java.awt.event.ActionListener
· javax.swing.event.ChangeListener
JDK 1.8 新增加的函数接口:
· java.util.function
java.util.function 它包含了很多类,用来支持 Java的函数式编程,该包中的函数式接口有:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
|
序号 接口 & 描述 1 BiConsumer<T,U> 代表了一个接受两个输入参数的操作,并且不返回任何结果 2 BiFunction<T,U,R> 代表了一个接受两个输入参数的方法,并且返回一个结果 3 BinaryOperator<T> 代表了一个作用于于两个同类型操作符的操作,并且返回了操作符同类型的结果 4 BiPredicate<T,U> 代表了一个两个参数的 boolean 值方法 5 BooleanSupplier 代表了 boolean 值结果的提供方 6 Consumer<T> 代表了接受一个输入参数并且无返回的操作 7 DoubleBinaryOperator 代表了作用于两个 double 值操作符的操作,并且返回了一个 double 值的结果。 8 DoubleConsumer 代表一个接受 double 值参数的操作,并且不返回结果。 9 DoubleFunction<R> 代表接受一个 double 值参数的方法,并且返回结果 10 DoublePredicate 代表一个拥有 double 值参数的 boolean 值方法 11 DoubleSupplier 代表一个 double 值结构的提供方 12 DoubleToIntFunction 接受一个 double 类型输入,返回一个 int 类型结果。 13 DoubleToLongFunction 接受一个 double 类型输入,返回一个 long 类型结果 14 DoubleUnaryOperator 接受一个参数同为类型 double ,返回值类型也为 double 。 15 Function<T,R> 接受一个输入参数,返回一个结果。 16 IntBinaryOperator 接受两个参数同为类型 int ,返回值类型也为 int 。 17 IntConsumer 接受一个 int 类型的输入参数,无返回值。 18 IntFunction<R> 接受一个 int 类型输入参数,返回一个结果。 19 IntPredicate :接受一个 int 输入参数,返回一个布尔值的结果。 20 IntSupplier 无参数,返回一个 int 类型结果。 21 IntToDoubleFunction 接受一个 int 类型输入,返回一个 double 类型结果。 22 IntToLongFunction 接受一个 int 类型输入,返回一个 long 类型结果。 23 IntUnaryOperator 接受一个参数同为类型 int ,返回值类型也为 int 。 24 LongBinaryOperator 接受两个参数同为类型 long ,返回值类型也为 long 。 25 LongConsumer 接受一个 long 类型的输入参数,无返回值。 26 LongFunction<R> 接受一个 long 类型输入参数,返回一个结果。 27 LongPredicate R接受一个 long 输入参数,返回一个布尔值类型结果。 28 LongSupplier 无参数,返回一个结果 long 类型的值。 29 LongToDoubleFunction 接受一个 long 类型输入,返回一个 double 类型结果。 30 LongToIntFunction 接受一个 long 类型输入,返回一个 int 类型结果。 31 LongUnaryOperator 接受一个参数同为类型 long ,返回值类型也为 long 。 32 ObjDoubleConsumer<T> 接受一个object类型和一个 double 类型的输入参数,无返回值。 33 ObjIntConsumer<T> 接受一个object类型和一个 int 类型的输入参数,无返回值。 34 ObjLongConsumer<T> 接受一个object类型和一个 long 类型的输入参数,无返回值。 35 Predicate<T> 接受一个输入参数,返回一个布尔值结果。 36 Supplier<T> 无参数,返回一个结果。 37 ToDoubleBiFunction<T,U> 接受两个输入参数,返回一个 double 类型结果 38 ToDoubleFunction<T> 接受一个输入参数,返回一个 double 类型结果 39 ToIntBiFunction<T,U> 接受两个输入参数,返回一个 int 类型结果。 40 ToIntFunction<T> 接受一个输入参数,返回一个 int 类型结果。 41 ToLongBiFunction<T,U> 接受两个输入参数,返回一个 long 类型结果。 42 ToLongFunction<T> 接受一个输入参数,返回一个 long 类型结果。 43 UnaryOperator<T> 接受一个参数为类型T,返回值类型也为T。 |
3.1 函数式接口实例
Predicate <T> 接口是一个函数式接口,它接受一个输入参数 T,返回一个布尔值结果。
该接口包含多种默认方法来将Predicate组合成其他复杂的逻辑(比如:与,或,非)。
该接口用于测试对象是 true 或 false。
我们可以通过以下实例(Java8Tester.java)来了解函数式接口 Predicate <T> 的使用:
Java8Tester.java文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
public class Java8Tester { public static void main(String args[]){ List<Integer> list = Arrays.asList( 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ); // Predicate<Integer> predicate = n -> true // n 是一个参数传递到 Predicate 接口的 test 方法 // n 如果存在则 test 方法返回 true System.out.println( "输出所有数据:" ); // 传递参数 n eval(list, n-> true ); // Predicate<Integer> predicate1 = n -> n%2 == 0 // n 是一个参数传递到 Predicate 接口的 test 方法 // 如果 n%2 为 0 test 方法返回 true System.out.println( "输出所有偶数:" ); eval(list, n-> n% 2 == 0 ); // Predicate<Integer> predicate2 = n -> n > 3 // n 是一个参数传递到 Predicate 接口的 test 方法 // 如果 n 大于 3 test 方法返回 true System.out.println( "输出大于 3 的所有数字:" ); eval(list, n-> n > 3 ); } public static void eval(List<Integer> list, Predicate<Integer> predicate) { for (Integer n: list) { if (predicate.test(n)) { System.out.println(n + " " ); } } } } |
执行以上脚本,输出结果为:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
1 2 3 4 5 6 7 8 9 输出所有偶数: 2 4 6 8 输出大于 3 的所有数字: 4 5 6 7 8 9 |
四、Java 8 默认方法
Java 8 新增了接口的默认方法。
简单说,默认方法就是接口可以有实现方法,而且不需要实现类去实现其方法。
我们只需在方法名前面加个default关键字即可实现默认方法。
为什么要有这个特性?
首先,之前的接口是个双刃剑,好处是面向抽象而不是面向具体编程,缺陷是,当需要修改接口时候,需要修改全部实现该接口的类,目前的java 8之前的集合框架没有foreach方法,通常能想到的解决办法是在JDK里给相关的接口添加新的方法及实现。然而,对于已经发布的版本,是没法在给接口添加新方法的同时不影响已有的实现。所以引进的默认方法。他们的目的是为了解决接口的修改与现有的实现不兼容的问题。
4.1语法
默认方法语法格式如下:
1
2
3
4
5
|
public interface vehicle { default void print() { System.out.println( "我是一辆车!" ); } } |
4.2多个默认方法
一个接口有默认方法,考虑这样的情况,一个类实现了多个接口,且这些接口有相同的默认方法,以下实例说明了这种情况的解决方法
1
2
3
4
5
|
public interface vehicle { default void print() { System.out.println( "我是一辆车!" ); } } |
1
2
3
4
5
|
public interface fourWheeler { default void print() { System.out.println( "我是一辆四轮车!" ); } } |
第一个解决方案是创建自己的默认方法,来覆盖重写接口的默认方法:
1
2
3
4
5
6
7
|
public class Car implements vehicle, fourWheeler { @Override public void print() { System.out.println( "我是一辆四轮汽车!" ); } } |
第二种解决方案可以使用 super 来调用指定接口的默认方法:
1
2
3
4
5
6
|
public class Car implements vehicle, fourWheeler { @Override public void print() { vehicle. super .print(); } } |
4.3 静态默认方法
Java 8 的另一个特性是接口可以声明(并且可以提供实现)静态方法。例如:
1
2
3
4
5
6
7
8
9
|
public interface vehicle { default void print() { System.out.println( "我是一辆车!" ); } // 静态方法 static void blowHorn() { System.out.println( "按喇叭!!!" ); } } |
4.4 默认方法实例
我们可以通过以下代码来了解关于默认方法的使用,可以将代码放入 Java8Tester.java 文件中:
Java8Tester.java文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
public class Java8Tester { public static void main(String args[]) { Vehicle vehicle = new Car(); vehicle.print(); } } interface Vehicle { default void print() { System.out.println( "我是一辆车!" ); } static void blowHorn() { System.out.println( "按喇叭!!!" ); } } interface FourWheeler { default void print() { System.out.println( "我是一辆四轮车!" ); } } class Car implements Vehicle, FourWheeler { public void print() { Vehicle. super .print(); FourWheeler. super .print(); Vehicle.blowHorn(); System.out.println( "我是一辆汽车!" ); } } |
执行以上脚本,输出结果为:
1
2
3
4
|
我是一辆车! 我是一辆四轮车! 按喇叭!!! 我是一辆汽车! |
五、Java 8 Stream
Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。
Stream使用一种类似用SQL语句从数据库查询数据的直观方式来提供一种对Java集合运算和表达的高阶抽象。
Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
这种风格将要处理的元素集合看作一种流,流在管道中传输,并且可以在管道的节点上进行处理,比如筛选,排序,聚合等。
元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。
5.1什么是 Stream?
Stream(流)是一个来自数据源的元素队列并支持聚合操作
元素:是特定类型的对象,形成一个队列。Java中的Stream并不会存储元素,而是按需计算。
数据源 :流的来源。可以是集合,数组,I/O channel,产生器generator等。
聚合操作: 类似SQL语句一样的操作,比如filter, map, reduce, find,match, sorted等。
和以前的Collection操作不同,Stream操作还有两个基础的特征:
Pipelining::中间操作都会返回流对象本身。这样多个操作可以串联成一个管道,如同流式风格(fluent style)。这样做可以对操作进行优化,比如延迟执行(laziness)和短路( short-circuiting)。
内部迭代:以前对集合遍历都是通过Iterator或者For-Each的方式,显式的在集合外部进行迭代,这叫做外部迭代。Stream提供了内部迭代的方式,通过访问者模式(Visitor)实现。
5.2生成流
在Java 8中,集合接口有两个方法来生成流:
stream() −为集合创建串行流。
parallelStream() − 为集合创建并行流。
1
2
3
4
|
public static void main(String[] args) { List<String> strings = Arrays.asList( "abc" , "" , "bc" , "efg" , "abcd" , "" , "jkl" ); List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList()); } |
5.3 forEach
Stream 提供了新的方法 'forEach' 来迭代流中的每个数据。以下代码片段使用forEach 输出了10个随机数:
1
2
|
Random random = new Random(); random.ints().limit( 10 ).forEach(System.out::println); |
5.4 map
map 方法用于映射每个元素到对应的结果,以下代码片段使用 map 输出了元素对应的平方数:
1
2
3
|
List<Integer> numbers = Arrays.asList( 3 , 2 , 2 , 3 , 7 , 3 , 5 ); // 获取对应的平方数 List<Integer> squaresList = numbers.stream().map(i -> i * i).distinct().collect(Collectors.toList()); |
5.5 filter
filter 方法用于通过设置条件过滤出元素。以下代码片段使用filter 方法过滤出空字符串:
1
2
3
|
List<String>strings = Arrays.asList( "abc" , "" , "bc" , "efg" , "abcd" , "" , "jkl" ); // 获取空字符串的数量 int count = ( int ) strings.stream().filter(string -> string.isEmpty()).count(); |
5.6 limit
limit 方法用于获取指定数量的流。以下代码片段使用 limit 方法打印出 10 条数据:
1
2
|
Random random = new Random(); random.ints().limit( 10 ).forEach(System.out::println); |
5.7 sorted
sorted 方法用于对流进行排序。以下代码片段使用 sorted 方法对输出的 10 个随机数进行排序:
1
2
|
Random random = new Random(); random.ints().limit( 10 ).sorted().forEach(System.out::println); |
5.8 并行(parallel)程序
parallelStream 是流并行处理程序的代替方法。以下实例我们使用parallelStream 来输出空字符串的数量:
1
2
3
|
List<String> strings = Arrays.asList( "abc" , "" , "bc" , "efg" , "abcd" , "" , "jkl" ); // 获取空字符串的数量 int count = ( int ) strings.parallelStream().filter(string -> string.isEmpty()).count(); |
我们可以很容易的在顺序运行和并行直接切换。
5.9 Collectors
Collectors 类实现了很多归约操作,例如将流转换成集合和聚合元素。Collectors可用于返回列表或字符串:
1
2
3
4
5
|
List<String> strings = Arrays.asList( "abc" , "" , "bc" , "efg" , "abcd" , "" , "jkl" ); List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList()); System.out.println( "筛选列表: " + filtered); String mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining( ", " )); System.out.println( "合并字符串: " + mergedString); |
5.10 统计
另外,一些产生统计结果的收集器也非常有用。它们主要用于int、double、long等基本类型上,它们可以用来产生类似如下的统计结果。
1
2
3
4
5
6
|
List<Integer> numbers = Arrays.asList( 3 , 2 , 2 , 3 , 7 , 3 , 5 ); IntSummaryStatistics stats = numbers.stream().mapToInt((x) -> x).summaryStatistics(); System.out.println( "列表中最大的数 : " + stats.getMax()); System.out.println( "列表中最小的数 : " + stats.getMin()); System.out.println( "所有数之和 : " + stats.getSum()); System.out.println( "平均数 : " + stats.getAverage()); |
5.11 Stream 完整实例
将以下代码放入Java8Tester.java 文件中:
Java8Tester.java文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
|
public class Java8Tester { public static void main(String args[]) { System.out.println( "使用 Java 7: " ); // 计算空字符串 List<String> strings = Arrays.asList( "abc" , "" , "bc" , "efg" , "abcd" , "" , "jkl" ); System.out.println( "列表: " + strings); long count = getCountEmptyStringUsingJava7(strings); System.out.println( "空字符数量为: " + count); count = getCountLength3UsingJava7(strings); System.out.println( "字符串长度为 3 的数量为: " + count); // 删除空字符串 List<String> filtered = deleteEmptyStringsUsingJava7(strings); System.out.println( "筛选后的列表: " + filtered); // 删除空字符串,并使用逗号把它们合并起来 String mergedString = getMergedStringUsingJava7(strings, ", " ); System.out.println( "合并字符串: " + mergedString); List<Integer> numbers = Arrays.asList( 3 , 2 , 2 , 3 , 7 , 3 , 5 ); // 获取列表元素平方数 List<Integer> squaresList = getSquares(numbers); System.out.println( "平方数列表: " + squaresList); List<Integer> integers = Arrays.asList( 1 , 2 , 13 , 4 , 15 , 6 , 17 , 8 , 19 ); System.out.println( "列表: " + integers); System.out.println( "列表中最大的数 : " + getMax(integers)); System.out.println( "列表中最小的数 : " + getMin(integers)); System.out.println( "所有数之和 : " + getSum(integers)); System.out.println( "平均数 : " + getAverage(integers)); System.out.println( "随机数: " ); // 输出10个随机数 Random random = new Random(); for ( int i = 0 ; i < 10 ; i++) { System.out.println(random.nextInt()); } System.out.println( "使用 Java 8: " ); System.out.println( "列表: " + strings); count = strings.stream().filter(string -> string.isEmpty()).count(); System.out.println( "空字符串数量为: " + count); count = strings.stream().filter(string -> string.length() == 3 ).count(); System.out.println( "字符串长度为 3 的数量为: " + count); filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList()); System.out.println( "筛选后的列表: " + filtered); mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining( ", " )); System.out.println( "合并字符串: " + mergedString); squaresList = numbers.stream().map(i -> i * i).distinct().collect(Collectors.toList()); System.out.println( "Squares List: " + squaresList); System.out.println( "列表: " + integers); IntSummaryStatistics stats = integers.stream().mapToInt((x) -> x).summaryStatistics(); System.out.println( "列表中最大的数 : " + stats.getMax()); System.out.println( "列表中最小的数 : " + stats.getMin()); System.out.println( "所有数之和 : " + stats.getSum()); System.out.println( "平均数 : " + stats.getAverage()); System.out.println( "随机数: " ); random.ints().limit( 10 ).sorted().forEach(System.out::println); // 并行处理 count = strings.parallelStream().filter(string -> string.isEmpty()).count(); System.out.println( "空字符串的数量为: " + count); } private static int getCountEmptyStringUsingJava7(List<String> strings) { int count = 0 ; for (String string : strings) { if (string.isEmpty()) { count++; } } return count; } private static int getCountLength3UsingJava7(List<String> strings) { int count = 0 ; for (String string : strings) { if (string.length() == 3 ) { count++; } } return count; } private static List<String> deleteEmptyStringsUsingJava7(List<String> strings) { List<String> filteredList = new ArrayList<String>(); for (String string : strings) { if (!string.isEmpty()) { filteredList.add(string); } } return filteredList; } private static String getMergedStringUsingJava7(List<String> strings, String separator) { StringBuilder stringBuilder = new StringBuilder(); for (String string : strings) { if (!string.isEmpty()) { stringBuilder.append(string); stringBuilder.append(separator); } } String mergedString = stringBuilder.toString(); return mergedString.substring( 0 , mergedString.length() - 2 ); } private static List<Integer> getSquares(List<Integer> numbers) { List<Integer> squaresList = new ArrayList<Integer>(); for (Integer number : numbers) { Integer square = new Integer(number.intValue() * number.intValue()); if (!squaresList.contains(square)) { squaresList.add(square); } } return squaresList; } private static int getMax(List<Integer> numbers) { int max = numbers.get( 0 ); for ( int i = 1 ; i < numbers.size(); i++) { Integer number = numbers.get(i); if (number.intValue() > max) { max = number.intValue(); } } return max; } private static int getMin(List<Integer> numbers) { int min = numbers.get( 0 ); for ( int i = 1 ; i < numbers.size(); i++) { Integer number = numbers.get(i); if (number.intValue() < min) { min = number.intValue(); } } return min; } private static int getSum(List numbers) { int sum = ( int ) (numbers.get( 0 )); for ( int i = 1 ; i < numbers.size(); i++) { sum += ( int ) numbers.get(i); } return sum; } private static int getAverage(List<Integer> numbers) { return getSum(numbers) / numbers.size(); } } |
执行以上脚本,输出结果为:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
使用Java7: 列表:[abc,, bc, efg, abcd,, jkl] 空字符数量为: 2 字符串长度为 3 的数量为: 3 筛选后的列表:[abc, bc, efg, abcd, jkl] 合并字符串: abc, bc, efg, abcd, jkl 平方数列表:[ 9 , 4 , 49 , 25 ] 列表:[ 1 , 2 , 13 , 4 , 15 , 6 , 17 , 8 , 19 ] 列表中最大的数: 19 列表中最小的数: 1 所有数之和: 85 平均数: 9 随机数: - 393170844 - 963842252 447036679 - 1043163142 - 881079698 221586850 - 1101570113 576190039 - 1045184578 1647841045 使用Java8: 列表:[abc,, bc, efg, abcd,, jkl] 空字符串数量为: 2 字符串长度为 3 的数量为: 3 筛选后的列表:[abc, bc, efg, abcd, jkl] 合并字符串: abc, bc, efg, abcd, jkl SquaresList:[ 9 , 4 , 49 , 25 ] 列表:[ 1 , 2 , 13 , 4 , 15 , 6 , 17 , 8 , 19 ] 列表中最大的数: 19 列表中最小的数: 1 所有数之和: 85 平均数: 9.444444444444445 随机数: - 1743813696 - 1301974944 - 1299484995 - 779981186 136544902 555792023 1243315896 1264920849 1472077135 1706423674 空字符串的数量为: 2 |
Stream的其他补充:https://my.oschina.net/mdxlcj/blog/1622718
六、Java 8 Optional 类
Optional 类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
Optional 是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。
Optional 类的引入很好的解决空指针异常。
6.1类声明
以下是一个 java.util.Optional<T> 类的声明:
publicfinalclassOptional<T> extendsObject
6.2 类方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
序号 方法 & 描述 1 static <T> Optional<T> empty() 返回空的 Optional 实例。 2 boolean equals(Object obj) 判断其他对象是否等于 Optional。 3 Optional<T> filter(Predicate<? super <T> predicate) 如果值存在,并且这个值匹配给定的 predicate,返回一个Optional用以描述这个值,否则返回一个空的Option Optional。 4 <U> Optional<U> flatMap(Function<? super T,Optional<U>> mapper) 如果值存在,返回基于Optional包含的映射方法的值,否则返回一个空的Optional 5 T get() 如果在这个Optional中包含这个值,返回值,否则抛出异常:NoSuchElementException 6 int hashCode() 返回存在值的哈希码,如果值不存在返回 0 。 7 void ifPresent(Consumer<? super T> consumer) 如果值存在则使用该值调用 consumer , 否则不做任何事情。 8 boolean isPresent() 如果值存在则方法会返回 true ,否则返回 false 。 9 <U>Optional<U> map(Function<? super T,? extends U> mapper) 如果存在该值,提供的映射方法,如果返回非 null ,返回一个Optional描述结果。 10 static <T> Optional<T> of(T value) 返回一个指定非 null 值的Optional。 11 static <T> Optional<T> ofNullable(T value) 如果为非空,返回 Optional 描述的指定值,否则返回空的 Optional。 12 T orElse(T other) 如果存在该值,返回值,否则返回 other。 13 T orElseGet(Supplier<? extends T> other) 如果存在该值,返回值,否则触发 other,并返回 other 调用的结果。 14 <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) 如果存在该值,返回包含的值,否则抛出由 Supplier 继承的异常 15 String toString() 返回一个Optional的非空字符串,用来调试 |
注意: 这些方法是从 java.lang.Object 类继承来的。
6.3 Optional 实例
我们可以通过以下实例来更好的了解 Optional 类的使用:
Java8Tester.java文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
public class Java8Tester { public static void main(String args[]) { Java8Tester java8Tester = new Java8Tester(); Integer value1 = null ; Integer value2 = new Integer( 10 ); // Optional.ofNullable - 允许传递为 null 参数 Optional<Integer> a = Optional.ofNullable(value1); // Optional.of - 如果传递的参数是 null,抛出异常 NullPointerException Optional<Integer> b = Optional.of(value2); System.out.println(java8Tester.sum(a, b)); } public Integer sum(Optional<Integer> a, Optional<Integer> b) { // Optional.isPresent - 判断值是否存在 System.out.println( "第一个参数值存在: " + a.isPresent()); System.out.println( "第二个参数值存在: " + b.isPresent()); // Optional.orElse - 如果值存在,返回它,否则返回默认值 Integer value1 = a.orElse( new Integer( 0 )); //Optional.get - 获取值,值需要存在 Integer value2 = b.get(); return value1 + value2; } } |
执行以上脚本,输出结果为:
1
2
3
|
第一个参数值存在: false 第二个参数值存在: true 10 |
七、Java 8 Nashorn JavaScript
Nashorn 一个 javascript 引擎。
从JDK1.8开始,Nashorn取代Rhino(JDK 1.6, JDK1.7)成为Java的嵌入式JavaScript引擎。Nashorn完全支持ECMAScript 5.1规范以及一些扩展。它使用基于JSR292的新语言特性,其中包含在JDK 7中引入的 invokedynamic,将JavaScript编译成Java字节码。
与先前的Rhino实现相比,这带来了2到10倍的性能提升。
7.1 jjs
jjs是个基于Nashorn引擎的命令行工具。它接受一些JavaScript源代码为参数,并且执行这些源代码。
例如,我们创建一个具有如下内容的sample.js文件:
1
|
print( 'Hello World!' ); |
打开控制台,输入以下命令:
1
|
$ jjs sample.js |
以上程序输出结果为:
1
|
HelloWorld! |
7.2 jjs 交互式编程
打开控制台,输入以下命令:
1
2
3
4
5
|
$ jjs jjs>print( "Hello, World!" ) Hello,World! jjs> quit() >> |
7.3 传递参数
1
2
3
4
5
6
|
打开控制台,输入以下命令: $ jjs -- a b c jjs>print( '字母: ' +arguments.join( ", " )) 字母: a, b, c jjs> |
7.4 Java中调用JavaScript
使用ScriptEngineManager,JavaScript代码可在Java中执行,如下:
Java8Tester.java文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
public class Java8Tester { public static void main(String args[]) { ScriptEngineManager scriptEngineManager = new ScriptEngineManager(); ScriptEngine nashorn = scriptEngineManager.getEngineByName( "nashorn" ); String name = "Runoob" ; Integer result = null ; try { nashorn.eval( "print('" + name + "')" ); result = (Integer) nashorn.eval( "10 + 2" ); } catch (ScriptException e) { System.out.println( "执行脚本错误: " + e.getMessage()); } System.out.println(result.toString()); } } |
执行以上脚本,输出结果为:
1
2
|
Runoob 12 |
7.5 JavaScript 中调用 Java
以下实例演示了如何在 JavaScript 中引用 Java 类:
1
2
3
4
5
6
7
8
9
10
11
12
|
varBigDecimal=Java.type( 'java.math.BigDecimal' ); function calculate(amount, percentage){ var result =newBigDecimal(amount).multiply( newBigDecimal(percentage)).divide(newBigDecimal( "100" ), 2 ,BigDecimal.ROUND_HALF_EVEN); return result.toPlainString(); } var result = calculate( 568000000000000000023 , 13.9 ); print(result); |
我们使用jjs 命令执行以上脚本,输出结果如下:
1
2
|
$ jjs sample.js 78952000000000002017.94 |
八、Java 8 日期时间 API
Java 8通过发布新的Date-Time API (JSR 310)来进一步加强对日期与时间的处理。
在旧版的Java 中,日期时间API 存在诸多问题,其中有:
· 非线程安全 − java.util.Date 是非线程安全的,所有的日期类都是可变的,这是Java日期类最大的问题之一。
· 设计很差 − Java的日期/时间类的定义并不一致,在java.util和java.sql的包中都有日期类,此外用于格式化和解析的类在java.text包中定义。java.util.Date同时包含日期和时间,而java.sql.Date仅包含日期,将其纳入java.sql包并不合理。另外这两个类都有相同的名字,这本身就是一个非常糟糕的设计。
· 时区处理麻烦 − 日期类并不提供国际化,没有时区支持,因此Java引入了java.util.Calendar和java.util.TimeZone类,但他们同样存在上述所有的问题。
Java 8 在 java.time 包下提供了很多新的 API。以下为两个比较重要的 API:
· Local(本地) − 简化了日期时间的处理,没有时区的问题。
· Zoned(时区) − 通过制定的时区处理日期时间。
新的java.time包涵盖了所有处理日期,时间,日期/时间,时区,时刻(instants),过程(during)与时钟(clock)的操作。
8.1 本地化日期时间 API
LocalDate/LocalTime 和 LocalDateTime 类可以在处理时区不是必须的情况。代码如下:
Java8Tester.java文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
public class Java8Tester { public static void main(String args[]) { Java8Tester java8tester = new Java8Tester(); java8tester.testLocalDateTime(); } public void testLocalDateTime() { // 获取当前的日期时间 LocalDateTime currentTime = LocalDateTime.now(); System.out.println( "当前时间: " + currentTime); LocalDate date1 = currentTime.toLocalDate(); System.out.println( "date1: " + date1); Month month = currentTime.getMonth(); int day = currentTime.getDayOfMonth(); int seconds = currentTime.getSecond(); System.out.println( "月: " + month + ", 日: " + day + ", 秒: " + seconds); LocalDateTime date2 = currentTime.withDayOfMonth( 10 ).withYear( 2012 ); System.out.println( "date2: " + date2); // 12 december 2014 LocalDate date3 = LocalDate.of( 2014 , Month.DECEMBER, 12 ); System.out.println( "date3: " + date3); // 22 小时 15 分钟 LocalTime date4 = LocalTime.of( 22 , 15 ); System.out.println( "date4: " + date4); // 解析字符串 LocalTime date5 = LocalTime.parse( "20:15:30" ); System.out.println( "date5: " + date5); } } |
执行以上脚本,输出结果为:
当前时间: 2018-06-08T15:19:16.910
date1:2018-06-08
月: JUNE, 日: 8, 秒: 16
date2:2012-06-10T15:19:16.910
date3:2014-12-12
date4:22:15
date5:20:15:30
8.2 使用时区的日期时间API
如果我们需要考虑到时区,就可以使用时区的日期时间API:
Java8Tester.java文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
public class Java8Tester { public static void main(String args[]) { Java8Tester java8tester = new Java8Tester(); java8tester.testZonedDateTime(); } public void testZonedDateTime() { // 获取当前时间日期 ZonedDateTime date1 = ZonedDateTime.parse( "2015-12-03T10:15:30+05:30[Asia/Shanghai]" ); System.out.println( "date1: " + date1); ZoneId id = ZoneId.of( "Europe/Paris" ); System.out.println( "ZoneId: " + id); ZoneId currentZone = ZoneId.systemDefault(); System.out.println( "当期时区: " + currentZone); } } |
执行以上脚本,输出结果为:
date1:2015-12-03T10:15:30+08:00[Asia/Shanghai]
ZoneId:Europe/Paris
当期时区: Asia/Shanghai
九、Java8 Base64
在Java8中,Base64编码已经成为Java类库的标准。
Java 8 内置了 Base64 编码的编码器和解码器。
Base64工具类提供了一套静态方法获取下面三种BASE64编解码器:
· 基本:输出被映射到一组字符A-Za-z0-9+/,编码不添加任何行标,输出的解码仅支持A-Za-z0-9+/。
· URL:输出映射到一组字符A-Za-z0-9+_,输出是URL和文件。
· MIME:输出隐射到MIME友好格式。输出每行不超过76字符,并且使用'\r'并跟随'\n'作为分割。编码输出最后没有行分割。
9.1 内嵌类

9.2 方法
注意:Base64 类的很多方法从 java.lang.Object 类继承。
9.3 Base64 实例
以下实例演示了Base64 的使用:
Java8Tester.java文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
public class Java8Tester { public static void main(String args[]) { try { // 使用基本编码 String base64encodedString = Base64.getEncoder().encodeToString( "runoob?java8" .getBytes( "utf-8" )); System.out.println( "Base64 编码字符串 (基本) :" + base64encodedString); // 解码 byte [] base64decodedBytes = Base64.getDecoder().decode(base64encodedString); System.out.println( "原始字符串: " + new String(base64decodedBytes, "utf-8" )); base64encodedString = Base64.getUrlEncoder().encodeToString( "TutorialsPoint?java8" .getBytes( "utf-8" )); System.out.println( "Base64 编码字符串 (URL) :" + base64encodedString); StringBuilder stringBuilder = new StringBuilder(); for ( int i = 0 ; i < 10 ; ++i) { stringBuilder.append(UUID.randomUUID().toString()); } byte [] mimeBytes = stringBuilder.toString().getBytes( "utf-8" ); String mimeEncodedString = Base64.getMimeEncoder().encodeToString(mimeBytes); System.out.println( "Base64 编码字符串 (MIME) :" + mimeEncodedString); } catch (UnsupportedEncodingException e) { System.out.println( "Error :" + e.getMessage()); } } } |
执行以上脚本,输出结果为:
1
2
3
4
5
6
7
8
9
10
|
Base64 编码字符串 (基本) :cnVub29iP2phdmE4 原始字符串: runoob?java8 Base64编码字符串(URL):VHV0b3JpYWxzUG9pbnQ_amF2YTg= Base64编码字符串(MIME):MjY5OGRlYmEtZDU0ZS00MjY0LWE3NmUtNzFiNTYwY2E4YjM1NmFmMDFlNzQtZDE2NC00MDk3LTlh ZjItYzNkNGJjNmQwOWE2OWM0NDJiN2YtOGM4Ny00MjhkLWJkMzgtMGVlZjFkZjkyYjJhZDUwYzk0 ZWMtNDE5ZC00MTliLWEyMTAtZGMyMjVkYjZiOTE3ZTkxMjljMTgtNjJiZC00YTFiLTg3MzAtOTA0 YzdjYjgxYjQ0YTUxOWNkMTAtNjgxZi00YjQ0LWFkZGMtMzk1YzRkZjIwMjcyMzA0MTQzN2ItYzBk My00MmQyLWJiZTUtOGM0MTlmMWIxM2MxYTY4NmNiOGEtNTkxZS00NDk1LThlN2EtM2RjMTZjMWJk ZWQyZTdhNmZiNDgtNjdiYy00ZmFlLThjNTYtMjcyNDNhMTRhZTkyYjNiNWY2MmEtNTZhYS00ZDhk LWEwZDYtY2I5ZTUwNzJhNGE1 |
十. CompletableFuture
10.1 Future
10.1.1 官方注释
// A Future represents the result of an asynchronous computation. Methods are provided to check if the computation iscomplete, to wait for its completion, and to retrieve the result ofthe computation. The result can only be retrieved using method get when the computation has completed, blocking ifnecessary until it is ready. Cancellation is performed by the cancel method. Additional methods are provided to determine if the task completed normally or was cancelled. Once acomputation has completed, the computation cannot be cancelled.If you would like to use a Future for the sake of cancellability but not provide a usable result, you can declare types of the form Future<?> and return null as a result of the underlying task.
Future表示异步计算结果。提供了检查计算是否完成、等待完成和获取计算结果的方法。
只能在计算完成后使用get方法获取结果,在计算完成前会阻塞。
通过cancel方法取消,并提供额外的方法确认任务是正常完成还是取消,一旦计算完成则不能取消。
如果想使用Future来取消但不提供可用的结果,可以声明null作为任务的返回结果。
10.1.2 示例代码
ExecutorService executor = Executors.newSingleThreadExecutor();
// 使用Future
Callable<String> callable = () -> {
return "task finised";
};
Future<String> future = executor.submit(callable);
String resp = future.get();
System.out.println("future.get = " + resp);
// 使用FutureTask
FutureTask<String> futureTask = new FutureTask<>(() -> {
return "futureTask finished";
});
executor.submit(futureTask);
resp = futureTask.get();
System.out.println("futureTask.get = " + resp);
executor.shutdown();
/* 结果
future.get = task finised
futureTask.get = futureTask finished
*/
10.2 CompletableFuture
10.2.1 官方注释
CompletableFuture:
// A Future that may be explicitly completed (setting its value and status), and may be used as a CompletionStage,supporting dependent functions and actions that trigger upon its completion.
CompletableFuture是一个可以显式完成并设置值和状态的Future,可以作为一个CompletionStage,以支持在完成时触发相关函数和操作。
// When two or more threads attempt to complete, completeExceptionally, or cancela CompletableFuture, only one of them succeeds.
当有两个或多个线程尝试完成、异常完成或取消CompletableFuture时,只有一个线程可以成功。
// In addition to these and related methods for directly manipulating status and results, CompletableFuture implements interface CompletionStage with the following policies:
除了这些直接操作状态和结果的相关方法外,CompletableFuture还使用以下策略实现CompletionStage接口:
// •Actions supplied for dependent completions of non-async methods may be performed by the thread that completes the current CompletableFuture, or by any other caller of a completion method.
•非异步方法相关的完成可以由处理完成当前CompletableFuture的线程提供执行,也可以由任何完成方法的调用者执行。
// •All async methods without an explicit Executor argument are performed using the ForkJoinPool.commonPool()(unless it does not support a parallelism level of at least two, in which case, a new Thread is created to run each task). To simplify monitoring, debugging, and tracking, all generated asynchronous tasks are instances of the marker interface AsynchronousCompletionTask.
•所有没有显式Executor参数的异步方法都使用ForkJoinPool.commonPool()执行[除非它不支持至少两个并行,在这种情况下会创建一个新线程来运行每个任务],为了简化监控、调试和跟踪,所有生成的异步任务都是标记接口AsynchronousCompletionTask的实例。
// •All CompletionStage methods are implemented independently of other public methods, so the behavior of one method is not impacted by overrides of others in sub classes.
•所有CompletionStage方法都与其他公共方法相独立,因此方法的行为不受子类的其他方法覆盖的影响。
// CompletableFuture also implements Future with the following policies:
CompletableFuture还使用以下策略实现Future:
// •Since (unlike FutureTask) this class has no direct control over the computation that causes it to be completed,cancellation is treated as just another form of exceptional completion. Method cancel has the same effect as completeExceptionally(new CancellationException()). Method isCompletedExceptionally can be used to determine if a CompletableFuture completed in any exceptional fashion.
•由于Futrue(与FutureTask不同)类无法直接控制导致其完成的计算,取消被视为另一种形式的异常完成。方法cancel与 completeExceptionally(new CancellationException())有相同的效果,方法isCompletedExceptionally可以用来确认CompletableFuture是否以异常方式完成。
// •In case of exceptional completion with a CompletionException,methods get() and get(long, TimeUnit) throw an ExecutionException with the same cause as held in the corresponding CompletionException. To simplify usage in most contexts, this class also defines methods join() and getNow that instead throw the CompletionException directly in these cases.
•如果出现CompletionException的异常完成,方法 get()和get(long, TimeUnit)会抛出ExecutionException,异常原因与对应的CompletionException中的原因相同。为了在多数情况下简化此类的使用,此类还定义了join()和getNow方法抛出CompletionException异常。
CompletionStage:
// A stage of a possibly asynchronous computation, that performs an action or computes a value when another CompletionStage completes.A stage completes upon termination of its computation, but this may in turn trigger other dependent stages. The functionality defined in this interface takes only a few basic forms, which expand out to a larger set of methods to capture a range of usage styles:
一个可能出现的异步计算的阶段,当另一个CompletionStage完成时执行一个动作或计算一个值。一个阶段在其计算终止时完成,但这可能反过来触发其他的依赖阶段。此接口中定义的功能仅采用几种基本形式,可扩展更多的方法集以支持一系列风格的使用。
// •The computation performed by a stage may be expressed as a Function, Consumer, or Runnable (using methods with names including apply, accept, or run, respectively) depending on whether it requires arguments and/or produces results.For example, stage.thenApply(x -> square(x)).thenAccept(x ->System.out.print(x)).thenRun(() -> System.out.println()). An additional form (compose) applies functions of stages themselves, rather than their results.
•阶段执行的计算可以表示为Function、Consumer或Runnable(分别使用apply、accept或run接口),具体取决于它是否需要参数and/or产生结果。例如:stage.thenApply (x -> square(x)).thenAccept(x ->System.out.print(x)).thenRun(() -> System.out.println())。处理阶段本身的函数处理而不是函数的结果。
// • One stage's execution may be triggered by completion of a single stage, or both of two stages, or either of two stages.Dependencies on a single stage are arranged using methods with prefix then. Those triggered by completion of both of two stages may combine their results or effects, using correspondingly named methods. Those triggered by either of two stages make no guarantees about which of the results or effects are used for the dependent stage'scomputation.
•一个阶段的执行可以由单个阶段的完成触发,或者两个阶段的完成或者两个阶段的任何一个触发。使用带有前缀then的方法来排列单个阶段的依赖关系,两个阶段可以使用both相关接口触发。使用combine相关接口合并结果和效果,由either触发两阶段不保管结果和效果的计算。
// • Dependencies among stages control the triggering of computations, but do not otherwise guarantee any particularordering. Additionally, execution of a new stage's computations maybe arranged in any of three ways: default execution, defaultasynchronous execution (using methods with suffix async that employ the stage's default asynchronous execution facility),or custom (via a supplied Executor). The execution properties of default and async modes are specified by CompletionStage implementations, not this interface. Methods with explicit Executor arguments may have arbitrary execution properties, and might not even support concurrent execution, but are arranged for processing in a way that accommodates asynchrony.
•阶段之间的依赖关系控制计算的触发,但不保证任何特定的顺序。此外,新阶段计算的执行可能以三种方式的任何一种方式:默认执行、默认异步执行[使用后缀async的方法,该方法采用阶段默认的异步执行工具]或自定义[通过提供的Executor]。默认和异步执行的执行属性由CompletionStage实现类指定,而不是该接口。具有显式Executor参数的方法可能具有任意的执行属性,甚至可能不支持并发执行,但以适应异步的方式处理。
// • Two method forms support processing whether the triggering stage completed normally or exceptionally: Method whenComplete allows injection of an action regardless of outcome, otherwise preserving the outcome in its completion. Method handle additionally allows the stage to compute a replacement result that may enable further processing by other dependent stages. In all other cases, if a stage's computation terminates abruptly with an (unchecked)exception or error, then all dependent stages requiring its completion complete exceptionally as well, with a CompletionException holding the exception as its cause. If a stage is dependent on both of two stages, and both complete exceptionally, then the CompletionException may correspond to either one of these exceptions. If a stage is dependent on either of two others, and only one of them completes exceptionally, no guarantees are made about whether the dependent stage completes normally or exceptionally. In the case of method whenComplete, when the supplied action itself encounters an exception, then the stage exceptionally completes with this exception if not already completed exceptionally.
•提供两种方法支持触发阶段是正常完成或异常完成:
whenComplete方法允许注入动作而忽略结果如何,否则在完成时保留结果。
handle方法还允许在阶段计算一个替代结果,该结果可以由其他相关阶段进行进一步处理。
在所有其他情况下,如果一个阶段的计算因unchecked异常或错误而突然终止,则依赖其执行结果的阶段也会异常完成,并抛出一个携带异常原因的CompletionException,如果一个阶段依赖于两个阶段,并且都异常完成,那么CompletionException可能对于与两阶段中的一个,并且只有其中一个异常完成,则无法保证依赖阶段是正常完成还是异常完成。在whenComplete方法中,当提供的阶段本身遇到异常,如果尚未完成异常,则阶段异常会完成此异常。
// All methods adhere to the above triggering, execution, and exceptional completion specifications (which are not repeated in individual method specifications). Additionally, while arguments used to pass a completion result (that is, for parameters of type T) for methods accepting them may be null, passing a null value for any other parameter will result in a NullPointerException being thrown.
所有触发都遵从上述的触发、执行和异常完成规范(在单个方法规范中不再重复)。此外虽然用于传递完成结果的参数T可能为空,但为其他方法传递null将会导致抛出NullPointerException。
// This interface does not define methods for initially creating,forcibly completing normally or exceptionally, probing completion status or results, or awaiting completion of a stage.Implementations of CompletionStage may provide means of achieving such effects, as appropriate. Method toCompletableFuture enables interoperability among different implementations of this interface by providing a common conversion type.
此接口未定义用于创建、强制正常和异常完成、探测完成状态和结果或等待阶段完成的方法。CompletionStage的实现自行考虑实现这些效果的方法。toCompletableFuture方法提供一个通用的转换类型来实现此接口的不同实现之间的互通性。
10.2.2 常用方法
10.2.2.1 创建
方法说明
用途 | 方法 |
---|---|
执行Runnable | runAsync(Runnable runnable) |
使用executor执行Runnable | runAsync(Runnable runnable, Executor executor) |
执行Supplier | supplyAsync(Supplier<U> supplier) |
使用executor执行Supplier | supplyAsync(Supplier<U> supplier, Executor executor) |
代码示例
ExecutorService executor = Executors.newSingleThreadExecutor(r -> new Thread(r, "test1"));
CompletableFuture.runAsync(() -> {
System.out.println("future1 finished:\t" + Thread.currentThread());
});
CompletableFuture.runAsync(() -> {
System.out.println("future2 finished:\t" + Thread.currentThread());
}, executor);
CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> {
System.out.println("future3 finished:\t" + Thread.currentThread());
return "future3";
});
System.out.println("future3 result:\t" + future3.get());
CompletableFuture<String> future4 = CompletableFuture.supplyAsync(() -> {
System.out.println("future4 finished:\t" + Thread.currentThread());
return "future4";
}, executor);
System.out.println("future4 result:\t" + future4.get());
executor.shutdown();
/* 结果
future1 finished: Thread[ForkJoinPool.commonPool-worker-9,5,main]
future2 finished: Thread[test1,5,main]
future3 finished: Thread[ForkJoinPool.commonPool-worker-9,5,main]
future3 result: future3
future4 finished: Thread[test1,5,main]
future4 result: future4
/
10.2.2.2 状态处理
方法说明
用途 | 方法 |
---|---|
取消执行,如果未执行则取消,已经执行不会取消 | cancel(boolean mayInterruptIfRunning) |
已经执行完成返回执行结果,未执行完成返回参数value | complete(T value) |
已经执行完成返回执行结果,未执行完成抛出异常 | completeExceptionally(Throwable ex) |
代码示例
CompletableFuture.runAsync(()->{
System.out.println("runAsync");
});
CompletableFuture<Integer> supplyAsync = CompletableFuture.supplyAsync(() -> {
System.out.println("runAsync start.");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("runAsync finished.");
return 1;
});
// supplyAsync.cancel(true);
Thread.sleep(2000);
// supplyAsync.completeExceptionally(new Exception("error"));
supplyAsync.complete(-1);
System.out.println("MainTest finished." + supplyAsync.get());
10.2.2.3 Then方法
方法说明
用途 | 方法 | 线程切换 |
---|---|---|
执行Runnable | thenRun(Runnable action) |
当前线程或Executor的线程 |
执行Runnable | thenRunAsync(Runnable action) |
Fork/Join线程 |
执行Runnable | thenRunAsync(Runnable action, Executor executor) |
Executor线程 |
执行Consumer | thenAccept(Consumer<? super T> action) |
当前线程或Executor的线程 |
执行Consumer | thenAcceptAsync(Consumer<? super T> action) |
Fork/Join线程 |
执行Consumer | thenAcceptAsync(Consumer<? super T> action, Executor executor) |
Executor线程 |
执行Function | thenApply(Function<? super T,? extends U> fn) |
当前线程或Executor的线程 |
执行Function | thenApplyAsync(Function<? super T,? extends U> fn) |
Fork/Join线程 |
执行Function | thenApplyAsync(Function<? super T,? extends U> fn, Executor executor) |
Executor线程 |
将当前和其他给定的阶段都执行完的结果作为两个参数传入Function | thenCombine(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn) |
当前线程或Executor的线程 |
将当前和其他给定的阶段都执行完的结果作为两个参数传入Function | thenCombineAsync(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn) |
Fork/Join线程 |
将当前和其他给定的阶段都执行完的结果作为两个参数传入Function | thenCombineAsync(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn, Executor executor) |
Executor线程 |
将当前和其他给定的阶段都执行完的结果作为两个参数传入Function | thenCombineAsync(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn, Executor executor) |
Executor线程 |
将当前阶段执行结果传入,返回一个CompletionStage | thenCompose(Function<? super T, ? extends CompletionStage<U>> fn) |
当前线程或Executor的线程 |
将当前阶段执行结果传入,返回一个CompletionStage | thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn) |
Fork/Join线程 |
将当前阶段执行结果传入,返回一个CompletionStage | thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn, Executor executor) |
Executor线程 |
代码示例
// thenRun、thenRunAsync
ExecutorService executor = Executors.newSingleThreadExecutor(r -> new Thread(r, "test1"));
ExecutorService executor2 = Executors.newSingleThreadExecutor(r -> new Thread(r, "test2"));
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
System.out.println("future finished:\t" + Thread.currentThread());
}, executor);
future.thenRun(() -> {
System.out.println("thenRun finished:\t" + Thread.currentThread());
});
future.thenRunAsync(() -> {
System.out.println("thenRunAsync finished:\t" + Thread.currentThread());
});
future.thenRunAsync(() -> {
System.out.println("thenRunAsync executor finished:\t" + Thread.currentThread());
}, executor2);
executor.shutdown();
executor2.shutdown();
/* 结果
future finished: Thread[test1,5,main]
thenRun finished: Thread[test1,5,main]
thenRunAsync finished: Thread[ForkJoinPool.commonPool-worker-9,5,main]
thenRunAsync executor finished: Thread[test2,5,main]
*/
// thenAccept、thenAcceptAsync
ExecutorService executor = Executors.newSingleThreadExecutor(r -> new Thread(r, "test1"));
ExecutorService executor2 = Executors.newSingleThreadExecutor(r -> new Thread(r, "test2"));
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
return "future";
}, executor);
future.thenAccept((t) -> {
System.out.println("thenAccept:\t" + Thread.currentThread());
});
future.thenAcceptAsync((t) -> {
System.out.println("thenAcceptAsync:\t" + Thread.currentThread());
});
future.thenAcceptAsync((t) -> {
System.out.println("thenAcceptAsync:\t" + Thread.currentThread());
}, executor2);
executor.shutdown();
executor2.shutdown();
/* 结果
thenAccept: Thread[main,5,main]
thenAcceptAsync: Thread[ForkJoinPool.commonPool-worker-9,5,main]
thenAcceptAsync: Thread[test2,5,main]
*/
// thenApply、thenApplyAsync
ExecutorService executor = Executors.newSingleThreadExecutor(r -> new Thread(r, "test1"));
ExecutorService executor2 = Executors.newSingleThreadExecutor(r -> new Thread(r, "test2"));
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
return "future";
}, executor);
CompletableFuture<String> future1 = future.thenApply((t) -> {
System.out.println("thenApply:\t" + Thread.currentThread());
return t + "-->thenApply";
});
System.out.println("future1:\t" + future1.get());
CompletableFuture<String> future2 = future.thenApplyAsync((t) -> {
System.out.println("thenApplyAsync:\t" + Thread.currentThread());
return t + "-->thenApplyAsync";
});
System.out.println("future2:\t" + future2.get());
CompletableFuture<String> future3 = future.thenApplyAsync((t) -> {
System.out.println("thenApplyAsync:\t" + Thread.currentThread());
return t + "-->thenApplyAsync";
}, executor2);
System.out.println("future3:\t" + future3.get());
executor.shutdown();
executor2.shutdown();
/* 结果
thenApply: Thread[main,5,main]
future1: future-->thenApply
thenApplyAsync: Thread[ForkJoinPool.commonPool-worker-9,5,main]
future2: future-->thenApplyAsync
thenApplyAsync: Thread[test2,5,main]
future3: future-->thenApplyAsync
*/
// thenCombine、thenCombineAsync
ExecutorService executor = Executors.newSingleThreadExecutor(r -> new Thread(r, "test1"));
ExecutorService executor2 = Executors.newSingleThreadExecutor(r -> new Thread(r, "test2"));
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
return "supplyAsync1";
}, executor);
future.get();
CompletableFuture<String> future1 = future.thenCombine(CompletableFuture.supplyAsync(() -> {
return "supplyAsync2";
}), (t1, t2) -> {
System.out.println("thenCombine:\t" + Thread.currentThread());
return t1 + "-->" + t2;
});
CompletableFuture<String> future2 = future.thenCombineAsync(CompletableFuture.supplyAsync(() -> {
return "supplyAsync2";
}), (t1, t2) -> {
System.out.println("thenCombineAsync:\t" + Thread.currentThread());
return t1 + "-->" + t2;
});
CompletableFuture<String> future3 = future.thenCombineAsync(CompletableFuture.supplyAsync(() -> {
return "supplyAsync2";
}), (t1, t2) -> {
System.out.println("thenCombineAsync:\t" + Thread.currentThread());
return t1 + "-->" + t2;
}, executor2);
System.out.println("future1=\t" + future1.get());
System.out.println("future2=\t" + future2.get());
System.out.println("future3=\t" + future3.get());
executor.shutdown();
executor2.shutdown();
/* 结果
thenCombine: Thread[main,5,main]
thenCombineAsync: Thread[ForkJoinPool.commonPool-worker-9,5,main]
future1= supplyAsync1-->supplyAsync2
future2= supplyAsync1-->supplyAsync2
thenCombineAsync: Thread[test2,5,main]
future3= supplyAsync1-->supplyAsync2
*/
// thenCompose、thenComposeAsync
ExecutorService executor = Executors.newSingleThreadExecutor(r -> new Thread(r, "test1"));
ExecutorService executor2 = Executors.newSingleThreadExecutor(r -> new Thread(r, "test2"));
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
return "supplyAsync1";
}, executor);
CompletableFuture<String> future2 = future.thenCompose((t) -> {
System.out.println("thenCompose:\t" + Thread.currentThread());
return CompletableFuture.supplyAsync(() -> {
return t + "-->supplyAsync2";
});
});
System.out.println("future2=\t" + future2.get());
CompletableFuture<String> future3 = future.thenComposeAsync((t) -> {
System.out.println("thenCompose:\t" + Thread.currentThread());
return CompletableFuture.supplyAsync(() -> {
return t + "-->supplyAsync2";
});
});
System.out.println("future3=\t" + future3.get());
CompletableFuture<String> future4 = future.thenComposeAsync((t) -> {
System.out.println("thenCompose:\t" + Thread.currentThread());
return CompletableFuture.supplyAsync(() -> {
return t + "-->supplyAsync2";
});
}, executor2);
System.out.println("future4=\t" + future4.get());
executor.shutdown();
executor2.shutdown();
/* 结果
thenCompose: Thread[main,5,main]
future2= supplyAsync1-->supplyAsync2
thenCompose: Thread[ForkJoinPool.commonPool-worker-9,5,main]
future3= supplyAsync1-->supplyAsync2
thenCompose: Thread[test2,5,main]
future4= supplyAsync1-->supplyAsync2
*/
Then···和then···Async区别
So thenRun may execute the action in either, the caller’s thread or the Executor’s thread where as the single-argument. thenRunAsync will always use the Fork/Join pool and only the two argument thenRunAsync will always use the provided executor.
ExecutorService executor = Executors.newSingleThreadExecutor(r -> new Thread(r, "test1"));
CompletableFuture<?> future = CompletableFuture.runAsync(() -> {
}, executor);
future.join();
future.thenRun(() -> System.out.println("thenRun:\t" + Thread.currentThread()));
future.thenRunAsync(() -> System.out.println("thenRunAsync:\t" + Thread.currentThread()));
future.thenRunAsync(() -> System.out.println("thenRunAsync+e:\t" + Thread.currentThread()), executor);
executor.shutdown();
/* 结果
thenRun: Thread[main,5,main]
thenRunAsync: Thread[ForkJoinPool.commonPool-worker-9,5,main]
thenRunAsync+e: Thread[test1,5,main]
*/
ExecutorService executor = Executors.newSingleThreadExecutor(r -> new Thread(r, "test1"));
CompletableFuture<?> future = CompletableFuture.runAsync(() -> LockSupport.parkNanos((int) 1e9), executor);
future.thenRun(() -> System.out.println("thenRun:\t" + Thread.currentThread()));
future.thenRunAsync(() -> System.out.println("thenRunAsync:\t" + Thread.currentThread()));
future.thenRunAsync(() -> System.out.println("thenRunAsync+e:\t" + Thread.currentThread()), executor);
LockSupport.parkNanos((int) 2e9);
executor.shutdown();
/* 结果
thenRun: Thread[test1,5,main]
thenRunAsync: Thread[ForkJoinPool.commonPool-worker-9,5,main]
thenRunAsync+e: Thread[test1,5,main]
*/
**注意事项:由于所有方法 ···(T t)
、···Async(T t)
、···Async(T t, Executor executor)
处理方式都与then···(T t)
、then···Async(T t)
、then···Async(T t, Executor executor)
一致,故后续的示例代码中不再对···Async(T t)
、···Async(T t, Executor executor)
方法进行测试。 **
10.2.2.4 Either&Both方法
方法说明
用途 | 方法 |
---|---|
当前阶段和传入阶段中任意一个执行完成,执行Runnable | runAfterEither(CompletionStage<?> other, Runnable action) runAfterEitherAsync(CompletionStage<?> other, Runnable action) runAfterEitherAsync(CompletionStage<?> other, Runnable action, Executor executor) |
当前阶段和传入阶段中任意一个执行完成,执行Consumer | acceptEither(CompletionStage<? extends T> other, Consumer<? super T> action) acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action) acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action, Executor executor) |
当前阶段和传入阶段中任意一个执行完成,执行Function | applyToEither(CompletionStage<? extends T> other, Function<? super T, U> fn) applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T, U> fn) applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T, U> fn, Executor executor) |
当前阶段和传入阶段中都执行完成,执行BiConsumer | thenAcceptBoth(CompletionStage<? extends U> other, BiConsumer<? super T, ? super U> action) thenAcceptBothAsync(CompletionStage<? extends U> other, BiConsumer<? super T, ? super U> action) thenAcceptBothAsync(CompletionStage<? extends U> other, BiConsumer<? super T, ? super U> action, Executor executor) |
当前阶段和传入阶段中都执行完成,执行Runnable | runAfterBoth(CompletionStage<?> other, Runnable action) runAfterBothAsync(CompletionStage<?> other, Runnable action) runAfterBothAsync(CompletionStage<?> other, Runnable action, Executor executor) |
代码示例
// runAfterEither
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
try {
Thread.sleep(new Random().nextInt(100));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("future runAsync");
});
future.runAfterEither(CompletableFuture.runAsync(() -> {
try {
Thread.sleep(new Random().nextInt(100));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("future1 runAsync");
}), () -> {
System.out.println("Action finished");
});
Thread.sleep(200);
/* 结果1
future1 runAsync
Action finished
future runAsync
结果2
future runAsync
Action finished
future1 runAsync
*/
// acceptEither
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(new Random().nextInt(100));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("future supplyAsync");
return "future";
});
future.acceptEither(CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(new Random().nextInt(100));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("future1 supplyAsync");
return "future1";
}), (t) -> {
System.out.println("Action finished: \t" + t);
});
Thread.sleep(200);
/* 结果1
future1 supplyAsync
Action finished: future1
future supplyAsync
结果2
future supplyAsync
Action finished: future
future1 supplyAsync
*/
// applyToEither
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(new Random().nextInt(100));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("future supplyAsync");
return "future";
});
CompletableFuture<String> finished = future.applyToEither(CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(new Random().nextInt(100));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("future1 supplyAsync");
return "future1";
}), (t) -> {
System.out.println("Action finished: \t" + t);
return "originator future:" + t;
});
System.out.println("finished: \t" + finished.get());
Thread.sleep(200);
/* 结果1
future supplyAsync
Action finished: future
finished: originator future:future
future1 supplyAsync
结果2
future1 supplyAsync
Action finished: future1
finished: originator future:future1
future supplyAsync
*/
// thenAcceptBoth
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(new Random().nextInt(100));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("future supplyAsync");
return "future";
});
future.thenAcceptBoth(CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(new Random().nextInt(100));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("future1 supplyAsync");
return "future1";
}), (t1, t2) -> {
System.out.println("Action finished: t1=" + t1 + "\t t2=" + t2);
});
Thread.sleep(200);
/* 结果1
future1 supplyAsync
future supplyAsync
Action finished: t1=future t2=future1
结果2
future supplyAsync
future1 supplyAsync
Action finished: t1=future t2=future1
*/
// runAfterBoth
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
try {
Thread.sleep(new Random().nextInt(100));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("future runAsync");
});
future.runAfterBoth(CompletableFuture.runAsync(() -> {
try {
Thread.sleep(new Random().nextInt(100));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("future1 runAsync");
}), () -> {
System.out.println("Action finished");
});
Thread.sleep(200);
/* 结果1
future1 runAsync
future runAsync
Action finished
结果2
future runAsync
future1 runAsync
Action finished
*/
10.2.2.5 When方法
方法说明
用途 | 方法 |
---|---|
当前阶段完成时,执行BiConsumer | whenComplete(BiConsumer<? super T, ? super Throwable> action) whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action) whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action, Executor executor) |
代码示例
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(new Random().nextInt(100));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("future supplyAsync");
return "future";
});
future.whenComplete((t, e) -> {
System.out.println("whenComplete: \t" + t);
});
Thread.sleep(200);
/* 结果
future supplyAsync
whenComplete: future
*/
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(new Random().nextInt(100));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("future supplyAsync");
int a = 1 / 0;
return "future";
});
future.whenComplete((t, e) -> {
System.out.println("whenComplete: " + t + " \t exception: " + e);
});
Thread.sleep(200);
/* 结果
future supplyAsync
whenComplete: null exception: java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
*/
10.2.2.6 Handle方法
方法说明
用途 | 方法 |
---|---|
当前阶段完成或异常完成,执行BiFunction | handle(BiFunction<? super T, Throwable, ? extends U> fn) handleAsync(BiFunction<? super T, Throwable, ? extends U> fn) handleAsync(BiFunction<? super T, Throwable, ? extends U> fn, Executor executor) |
代码示例
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
return "future";
});
CompletableFuture<String> handle = future.handle((t, e) -> {
return "handle: " + t + " \t exception: " + e;
});
System.out.println(handle.get());
Thread.sleep(200);
/* 结果
handle: future exception: null
*/
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
int a = 1 / 0;
return "future";
});
CompletableFuture<String> handle = future.handle((t, e) -> {
return "handle: " + t + " \t exception: " + e;
});
System.out.println(handle.get());
Thread.sleep(200);
/* 结果
handle: null exception: java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
*/
十一.map的性能改善
JDK1.8版本之前,hashMap采用数组+链表方式实现,即使负载因子和Hash算法设计的再合理,也免不了会出现拉链过长的情况,一旦出现拉链过长,则会严重影响HashMap的性能。于是,在JDK1.8版本中,对数据结构做了进一步的优化,引入了红黑树。而当链表长度太长(默认超过8)时,链表就转换为红黑树,利用红黑树快速增删改查的特点提高HashMap的性能,其中会用到红黑树的插入、删除、查找等算法。
当插入新元素时,对于红黑树的判断如下:
判断table[i] 是否为treeNode,即table[i] 是否是红黑树,如果是红黑树,则直接在树中插入键值对,否则转向下面;
遍历table[i],判断链表长度是否大于8,大于8的话把链表转换为红黑树,在红黑树中执行插入操作,否则进行链表的插入操作;遍历过程中若发现key已经存在直接覆盖value即可;
其他更新详见官网:https://www.oracle.com/java/technologies/javase/8-whats-new.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具