JAVA8特性整理

新特性简介

速度更快

代码更少,更简洁(增加了新的语法支持:lambda表达式)

强大的Stream API

便于并行

最大化减少空指针异常(增加Optional类)

==其中最为核心的为 Lambda 表达式与Stream API==

Lambda 表达式

为啥使用lambda表达式?

Lambda 是一个 匿名函数,我们可以把 Lambda表达式理解为是 一段可以传递的代码(将代码像数据一样进行传递)。可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。

重要体现——匿名内部类

从匿名内部类到lambda的转换

匿名内部类

1
2
3
4
5
6
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("haha");
}
};

用lambda表达式表示匿名内部类

1
2
3
Runnable runnable1 = ()->{
System.out.println("hello");
};

匿名内部类参数传递

1
2
3
4
5
6
TreeSet<String> treeSet = new TreeSet<>(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return Integer.compare(o1.length(),o2.length());
}
});

lambda表达式表示匿名内部类参数传递

1
2
3
4
5
TreeSet<String> treeSet1 = new TreeSet<>(
(o1,o2)->{
return Integer.compare(o1.length(), o2.length());
}
);

lambda表达式语法

Lambda 表达式在Java 语言中引入了一个新的语法元素和操作符。这个操作符为 “- -> >” , 该操作符被称为Lambda 操作符或剪头操作符。它将 Lambda 分为两个部分:

左侧:指定了 Lambda 表达式需要的所有参数

右侧:指定了 Lambda 体,即 Lambda 表达式要执行的功能。

==语法格式:==

类型推断

上述 Lambda 表达式中的参数类型都是由编译器推断得出的。Lambda 表达式中无需指定类型,程序依然可以编译,这是因为 javac 根据程序的上下文,在后台推断出了参数的类型。Lambda 表达式的类型依赖于上下文环境,是由编译器推断出来的。这就是所谓的“类型推断”.

函数式接口

啥是函数式接口

  • 只包含一个抽象方法的接口,称为 函数式接口。
  • 你可以通过 Lambda 表达式来创建该接口的对象。(若 Lambda表达式抛出一个受检异常,那么该异常需要在目标接口的抽象方法上进行声明)。
  • 我们可以在任意函数式接口上使用 ==@FunctionalInterface 注解==,这样做可以检查它是否是一个函数式接口,同时 javadoc 也会包含一条声明,说明这个接口是一个函数式接口。

自定义函数式接口

作为参数传递lambda表达式

Java 内置四大核心函数式接口

函数式接口参数类型返回类型用途
Consumer
消费型接口
Tvoid对类型为T的对象应用操作,包含方法:void accept(T t)
Supplier
供给型接口
T返回类型为T的对象,包含方法:T get();
Function<T, R>
函数型接口
TR对类型为T的对象应用操作,并返回结果。结果是R类型的对象。包含方法:R apply(T t);
Predicate
断定型接口
Tboolean确定类型为T的对象是否满足某约束,并返回boolean 值。包含方法boolean test(T t);

3方法引用与构造器引用

方法引用

当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!(实现抽象方法的参数列表,必须与方法引用方法的参数列表保持一致!)方法引用:使用操作符 “ ::” 将方法名和对象或类的名字分隔开来。
如下三种主要使用情况 :

  • ==对象 :: 实例方法==
  • ==类 :: 静态方法==
  • ==类 ::==

构造器引用

格式: ClassName :: new
与函数式接口相结合,自动与函数式接口中方法兼容。可以把构造器引用赋值给定义的方法,与构造器参数
列表要与接口中抽象方法的参数列表一致!

数组引用

格式: type[] :: new

强大的 Stream API

了解 Stream

Java8中有两大最为重要的改变。第一个是 Lambda 表达式;另外一个则是 Stream API( java.util.stream .*) 。
==Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。==

4.2什么是 Stream

流 (Stream) 到底是什么呢 ?
是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。
“集合讲的是数据,流讲的是计算! ”
注意:
①Stream 自己不会存储元素。
②Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。

4.3创建Stream

  • Java8 中的 Collection 接口被扩展,提供了两个获取流的方法 :
    • default Streamstream() : 返回一个顺序流
    • default StreamparallelStream() : 返回一个并行流

由数组创建流Java8 中的 Arrays 的静态方法 stream() 可以获取数组流:
 staticStreamstream(T[] array): 返回一个流
重载形式,能够处理对应基本类型的数组:
 public static IntStream stream(int[] array)
 public static LongStream stream(long[] array)
 public static DoubleStream stream(double[] array)

4.4由值创建流

可以使用静态方法 Stream.of(), 通过显示值创建一个流。它可以接收任意数量的参数。
 public staticStreamof(T… values) : 返回一个流

4.5由函数创建流:创建无限流

可以使用静态方法 Stream.iterate() 和Stream.generate(), 创建无限流。
 迭代
public staticStreamiterate(final T seed, finalUnaryOperatorf)
 生成
public staticStreamgenerate(Suppliers) ;

4.6Stream 的中间操作

==多个 中间操作可以连接起来形成一个 流水线,除非流水线上触发终止操作,否则 中间操作不会执行任何的 处理!而在 终止操作时一次性全部 处理,称为“惰性求值”。==

4.7Stream的终止操作

终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是 void.

Collector 接口中方法的实现决定了如何对流执行收集操作(如收集到 List、Set、Map)。但是 Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例,具体方法与实例如下表:

4.8并行 流与 串行 流

==并行流 就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流==。Java 8 中将并行进行了优化,我们可以很容易的对数据进行并行操作。Stream API 可以声明性地通过 parallel() 与sequential() 在并行流与顺序流之间进行切换。

4.9练习Demo

创建Stream 的四种方式

  1). 通过Collection得Stream()方法(串行流)或者 parallelStream()方法(并行流)创建Stream。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 创建 Stream的四种方式
* 1.通过Collection得Stream()方法(串行流)
或者 parallelStream()方法(并行流)创建Stream
*/
@Test
public void test1 () {

//1. 通过Collection得Stream()方法(串行流)
//或者 parallelStream()方法(并行流)创建Stream
List<String> list = new ArrayList<String>();
Stream<String> stream1 = list.stream();

Stream<String> stream2 = list.parallelStream();

}

2).通过Arrays中得静态方法stream()获取数组流

1
2
3
4
5
6
7
8
9
10
11
/**
* 创建 Stream的四种方式
* 2. 通过Arrays中得静态方法stream()获取数组流
*/
@Test
public void test2 () {

//2. 通过Arrays中得静态方法stream()获取数组流
IntStream stream = Arrays.stream(new int[]{3,5});

}

3). 通过Stream类中得 of()静态方法获取流

1
2
3
4
5
6
7
8
9
10
11
/**
* 创建 Stream的四种方式
* 3. 通过Stream类中得 of()静态方法获取流
*/
@Test
public void test3 () {

//3. 通过Stream类中得 of()静态方法获取流
Stream<String> stream = Stream.of("4645", "huinnj");

}

4). 创建无限流(迭代、生成)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 创建 Stream的四种方式
* 4. 创建无限流(迭代、生成)
*/
@Test
public void test4 () {

//4. 创建无限流
//迭代(需要传入一个种子,也就是起始值,然后传入一个一元操作)
Stream<Integer> stream1 = Stream.iterate(2, (x) -> x * 2);

//生成(无限产生对象)
Stream<Double> stream2 = Stream.generate(() -> Math.random());

}

相关操作

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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
package com._521it.StreamAPI;
import com._521it.StreamAPI.Person;
import org.junit.Test;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
/**
* @Auther: yangxiangnan
* @Date: 2019/7/2 17:47
* @Description:
*/
public class StreamAPIDemo {
List<Person> list = Arrays.asList(new Person[]{
new Person("1", 10),
new Person("2", 20),
new Person("3", 30),
new Person("3", 30),
new Person("4", 40),
new Person("5", 50),
});
List<Person> persons = Arrays.asList(
new Person("张三", "男", 76, Status.FREE),
new Person("李四", "女", 12, Status.BUSY),
new Person("王五", "男", 35, Status.BUSY),
new Person("赵六", "男", 3, Status.FREE),
new Person("钱七", "男", 56, Status.BUSY),
new Person("翠花", "女", 34, Status.VOCATION),
new Person("翠花", "女", 34, Status.FREE),
new Person("翠花", "女", 34, Status.VOCATION)
);
/**
* 创建 Stream的四种方式
* 1.通过Collection得Stream()方法(串行流)
或者 parallelStream()方法(并行流)创建Stream
*/
@Test
public void test1 () {
//1. 通过Collection得Stream()方法(串行流)
//或者 parallelStream()方法(并行流)创建Stream
List<String> list = new ArrayList<String>();
Stream<String> stream1 = list.stream();
Stream<String> stream2 = list.parallelStream();
}

/**
* 创建 Stream的四种方式
* 2. 通过Arrays中得静态方法stream()获取数组流
*/
@Test
public void test2 () {
//2. 通过Arrays中得静态方法stream()获取数组流
IntStream stream = Arrays.stream(new int[]{3,5});
}

/**
* 创建 Stream的四种方式
* 3. 通过Stream类中得 of()静态方法获取流
*/
@Test
public void test3 () {
//3. 通过Stream类中得 of()静态方法获取流
Stream<String> stream = Stream.of("4645", "huinnj");
}

/**
* 创建 Stream的四种方式
* 4. 创建无限流(迭代、生成)
*/
@Test
public void test4 () {
//4. 创建无限流
//迭代(需要传入一个种子,也就是起始值,然后传入一个一元操作)
Stream<Integer> stream1 = Stream.iterate(2, (x) -> x * 2);
//生成(无限产生对象)
Stream<Double> stream2 = Stream.generate(() -> Math.random());
}

/**
* 筛选与切片
* filter —— 接收Lambda ,从流中排除某些元素。
*
*/
@Test
public void test5 () {
List<Person> list = Arrays.asList(new Person[]{
new Person("1", 10),
new Person("2", 20),
new Person("3", 30),
new Person("4", 40),
new Person("5", 50),
});
//内部迭代:在此过程中没有进行过迭代,由Stream api进行迭代
//中间操作:不会执行任何操作
Stream<Person> stream = list.stream().filter((e) -> {
System.out.println("Stream API 中间操作");
return e.getAge() > 20;
});
//终止操作:只有执行终止操作才会执行全部。即:延迟加载
stream.forEach(System.out :: println);

}
@Test
public void test6 () {
List<Person> list = Arrays.asList(new Person[]{
new Person("1", 10),
new Person("2", 20),
new Person("3", 30),
new Person("4", 40),
new Person("5", 50),
});
//外部迭代
Iterator<Person> it = list.iterator();
while (it.hasNext()) {
Person person = it.next();
if(person.getAge()>20){
System.out.println(person);
}
}

}
/**
* limit —— 截断流,使其元素不超过给定数量。
*/
@Test
public void test7 () {
//过滤之后取2个值
list.stream()
.filter((e)->e.getAge()>30)
.limit(2).forEach(System.out::println);

}
/**
* skip(n)—— 跳过元素,返回一个扔掉了前n个元素的流。
* 若流中元素不足n个,则返回一个空,与limit(n)互补。
*/
@Test
public void test8 () {
//跳过前2个值
list.stream().skip(2).forEach(System.out::println);
}
/**
* distinct —— 筛选,通过流所生成元素的hashCode()和equals()去除重复元素
*注意:distinct 需要实体中重写hashCode()和 equals()方法才可以使用
*/
@Test
public void test9 () {
list.stream().distinct().forEach(System.out::println);
}
/**
* . map ,将元素转换成其他形式或者提取信息。接收一个函数作为参数,该函数会被应用到每个元素 * 上,并将其映射成一个新的元素。
* map —— 映射 ,将元素转换成其他形式或者提取信息。
* 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
*/
@Test
public void test10 () {
//将流中每一个元素都映射到map的函数中,每个元素执行这个函数,再返回
List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd");
list.stream().map((e)->e.toUpperCase()).forEach(System.out::println);

//获取Person中的每一个人得名字name,再返回一个集合
List<String> stringList = this.list.stream().map(Person::getName).collect(Collectors.toList());
stringList.stream().forEach(System.out::println);
}
/**
* flatMap —— 接收一个函数作为参数,将流中的每个值都换成一个流,然后把所有流连接成一个流
*/
@Test
public void test11 () {
StreamAPIDemo s = new StreamAPIDemo();
List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd");
list.stream().flatMap((e) -> s.filterCharacter(e)).forEach(System.out::println);

//如果使用map则需要这样写
list.stream().map((e) -> s.filterCharacter(e)).forEach((e) -> {
e.forEach(System.out::println);
});
}
/*
其实map方法就相当于Collaction的add方法,如果add的是个集合得话就会变成二维数组,而flatMap 的话就相当于Collaction的addAll方法,参数如果是集合得话,只是将2个集合合并,而不是变成二维数组。
*/
/**
* sorted有两种方法,一种是不传任何参数,叫自然排序,还有一种需要传Comparator 接口参数,叫做定制排序。
*/
@Test
public void test12 () {
// 自然排序
List<Person> persons1 = list.stream().sorted().collect(Collectors.toList());
System.out.println(persons1);
//定制排序
List<Person> persons2 = list.stream().sorted((e1, e2) -> {
if (e1.getAge() == e2.getAge()) {
return 0;
} else if (e1.getAge() > e2.getAge()) {
return 1;
} else {
return -1;
}
}).collect(Collectors.toList());
persons2.stream().forEach(System.out::println);
}
/**
* allMatch —— 检查是否匹配所有元素。
* 判断所有状态是否都是FREE
*/
@Test
public void test13 () {
boolean isAllBusy = persons.stream().allMatch((e) -> Status.FREE.equals(e.getStatus()));
System.out.println(isAllBusy);
}
/**
* anyMatch —— 检查是否至少匹配所有元素。
* 判断是否有一个是FREE
*/
@Test
public void test14 () {
boolean b = persons.stream().anyMatch((e) -> Status.FREE.equals(e.getStatus()));
System.out.println(b);
}
/**
* noneMatch —— 检查是否没有匹配所有元素。
* 判断是否没有FREE
*/
@Test
public void test15 () {
boolean b = persons.stream().noneMatch((e) -> Status.FREE.equals(e.getStatus()));
System.out.println(b);
}
/**
* findFirst —— 返回第一个元素。
*
*/
@Test
public void test16 () {
Optional<Person> person = persons.stream().findFirst();
person.orElse(new Person("王五", "男", 35, Status.BUSY));
System.out.println(person);

}
/**
* findAny —— 返回当前流中任意元素。
*/
@Test
public void test17 () {
Optional<Person> person = persons.stream().findAny();
person.orElse(new Person("王五", "男", 35, Status.BUSY));
System.out.println(person);
}
/**
* count —— 返回流中元素总个数。
*/
@Test
public void test18 () {
long count = persons.stream().count();
System.out.println(count);

}
/**
* max —— 返回流中最大值。
*/
@Test
public void test19 () {
Optional<Person> person = persons.stream().max((e1, e2) -> Integer.compare(e1.getAge(), e2.getAge()));
System.out.println(person);

}
/**
* min —— 返回流中最小值。
*/
@Test
public void test20 () {
Optional<Person> person = persons.stream().min((e1, e2) -> Double.compare(e1.getAge(), e2.getAge()));
System.out.println(person);

}
/**
* reduce(T identitty,BinaryOperator)首先,需要传一个起始值,然后,传入的是一个二元运算。
*/
@Test
public void test21 () {
List<Integer> list = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Integer sum = list.stream().reduce(0,Integer::sum);
System.out.println(sum);
}

/**
* reduce(BinaryOperator)此方法相对于上面方法来说,没有起始值,则有可能结果为空,
* 所以返回的值会被封装到Optional中
*/
@Test
public void test22 () {
List<Integer> list = Arrays.asList();
Optional<Integer> sum = list.stream().reduce(Integer :: sum);
sum.orElse(0);
System.out.println(sum);
}

/**
* 将一个字符串转换为流
* @param str
* @return
*/
public Stream<Character> filterCharacter(String str){
List<Character> list = new ArrayList<>();
for (Character ch : str.toCharArray()) {
list.add(ch);
}
return list.stream();
}

}

5.接口中的默认方法与静态方法

5.1接口中的默认方法

==Java 8中允许接口中包含具有具体实现的方法,该方法称为“默认方法”,默认方法使用 default 关键字修饰。==

==接口默认方法的 ” 类优先 ” 原则==
若一个接口中定义了一个默认方法,而另外一个父类或接口中又定义了一个同名的方法时
 选择父类中的方法。如果一个父类提供了具体的实现,那么接口中具有相同名称和参数的默认方法会被忽略。
 接口冲突。如果一个父接口提供一个默认方法,而另一个接口也提供了一个具有相同名称和参数列表的方法(不管方法是否是默认方法),那么必须覆盖该方法来解决冲突

5.2接口中的静态方法

Java8 中,接口中允许添加静态方法。

6.Optional 类

Optional类(java.util.Optional) 是一个容器类,代表一个值存在或不存在,原来用 null 表示一个值不存在,现在 Optional 可以更好的表达这个概念。并且可以避免空指针异常。
常用方法:
Optional.of(T t) : 创建一个 Optional 实例
Optional.empty() : 创建一个空的 Optional 实例
Optional.ofNullable(T t):若 t 不为 null,创建 Optional 实例,否则创建空实例
isPresent() : 判断是否包含值
orElse(T t) : 如果调用对象包含值,返回该值,否则返回t
orElseGet(Supplier s) :如果调用对象包含值,返回该值,否则返回 s 获取的值
map(Function f): 如果有值对其处理,并返回处理后的Optional,否则返回 Optional.empty()
flatMap(Function mapper):与 map 类似,要求返回值必须是Optional

一、Optional的构造方式:

1、Optional.of(T)

该方式的入参不能为null,否则会有NPE,在确定入参不为空时使用该方式。

2、Optional.ofNullable(T)

该方式的入参可以为null,当入参不确定为非null时使用。

3、Optional.empty()

这种方式是返回一个空Optional,等效Optional.ofNullable(null)

二、 如何正确使用Optional

尽量避免使用的地方:

1、避免使用Optional.isPresent()来检查实例是否存在,因为这种方式和null != obj没有区别,这样用就没什么意义了。

2、避免使用Optional.get()方式来获取实例对象,因为使用前需要使用Optional.isPresent()来检查实例是否存在,否则会出现NPE问题。

3、避免使用Optional作为类或者实例的属性,而应该在返回值中用来包装返回实例对象。

4、避免使用Optional作为方法的参数,原因同3。

正确使用方式:

1、实例对象存在则返回,否则提供默认值或者通过方法来设置返回值,即使用orElse/orElseGet方式:

1
2
3
4
5
 //存在则返回
User king = new User(1, "king");
Optional<User> userOpt = Optional.of(king);
User user = userOpt.orElse(null);
System.out.println(user.getName());
1
2
3
4
5
 //不存在提供默认值
User user2 = null;
Optional<User> userOpt2 = Optional.ofNullable(user2);
User user3 = userOpt2.orElse(unknown);
System.out.println(user3.getName());
1
2
3
 //通过方法提供值
User user4 = userOpt2.orElseGet(() -> new User(0, "DEFAULT"));
System.out.println(user4.getName())

不建议这样使用:

1
2
3
4
5
if(userOpt.isPresent()) {
System.out.println(userOpt.get().getName());
} else {
//。。。
}

2、使用ifPresent()来进行对象操作,存在则操作,否则不操作。

1
2
3
//实例存在则操作,否则不操作
userOpt.ifPresent(u -> System.out.println(u.getName()));
userOpt2.ifPresent(u -> System.out.println(u.getName()));

3、使用map/flatMap来获取关联数据

1
2
3
4
5
6
7
8
9
10
11
//使用map方法获取关联数据
System.out.println(userOpt.map(u -> u.getName()).orElse("Unknown"));
System.out.println(userOpt2.map(u -> u.getName()).orElse("Default"));
//使用flatMap方法获取关联数据
List<String> interests = new ArrayList<String>();
interests.add("a");interests.add("b");interests.add("c");
user.setInterests(interests);
List<String> interests2 = Optional.of(user)
.flatMap(u -> Optional.ofNullable(u.getInterests()))
.orElse(Collections.emptyList());
System.out.println(interests2.isEmpty());

7.重复注解与类型注解

Java 8对注解处理提供了两点改进:==可重复的注解及可用于类型的注解。==


JAVA8特性整理
https://github.com/yangxiangnanwill/yangxiangnanwill.github.io/2024/01/03/好好码代码吖/JAVA/JAVA特性/JAVA8特性整理/
作者
will
发布于
2024年1月3日
许可协议