JDK1.8新特性

JDK1.8新特性

JDK1.8新特性简介

  • 速度更快 - 优化底层源码,比如HashMap、ConcurrentHashMap
  • 代码更少 - 添加新的语法Lambda表达式
  • 强大的Stream API
  • 便于并行
  • 最大化减少空指针异常 - Optional

Lambda表达式

简介

Lambda是一个匿名函数(方法), 允许把函数作为一个方法的参数 。利用Lambda表达式可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。

一般都是优化匿名内部类

基础语法

无参数、无返回值的抽象方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Test1 {
@Test
public void test01() {
// I1 i1 = new I1() {
// @Override
// public void method() {
// System.out.println("传统使用匿名内部类的方式");
// }
// };
// i1.method();

I1 i1 = ()-> System.out.println("采用Lambda表达式的方式");
i1.method();
}
}
interface I1{
public void method();//无参数、无返回值的抽象方法
}

一个参数、无返回值的抽象方法

1
2
3
4
5
6
7
8
9
10
public class Test1 {
@Test
public void test01() {
I1 i1 = (x)-> System.out.println("采用Lambda表达式的方式 " + x);
i1.method(1000);
}
}
interface I1{
public void method(int num1);//一个参数、无返回值的抽象方法
}

多个参数、无返回值的抽象方法

1
2
3
4
5
6
7
8
9
10
11
12
public class Test1 {
@Test
public void test01() {
I1 i1 = (x,y,z)->
System.out.println("采用Lambda表达式的方式 " + x + y + z);
i1.method(1000,2000,3000);
}
}
interface I1{
//多个参数、无返回值的抽象方法
public void method(int num1,int num2,int num3);
}

多个参数、有返回值的抽象方法

1
2
3
4
5
6
7
8
9
10
11
12
public class Test1 {
@Test
public void test01() {
I1 i1 = (x,y,z)-> x+y+z;
int result = i1.method(1000,2000,3000);
System.out.println(result);
}
}
interface I1{
//多个参数、有返回值的抽象方法
public int method(int num1,int num2,int num3);
}

注意点

  1. 重写方法的形参只有一个时,可以不加小括号
  2. Lambda表达式当中不允许声明一个与局部变量同名的参数或者局部变量
  3. Lambda表达式中访问外层的局部变量,外层的局部变量自动变成隐式常量,默认添加final
  4. 重写方法的形参同时加类型或同时不加类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Test1 {
@Test
public void test01() {
int x;
int num = 10;
I1 i1 = x -> System.out.println(x + (num++));
i1.method(1000);

I2 i2 = (int x,int y) -> {
int result = x+y;
return result;
};
int result = i2.method(10, 20);
System.out.println(result);
}
}
interface I1{
public void method(int num1);
}
interface I2{
public int method(int num1,int num2);
}

练习

1.调用Collections.sort()方法,通过定制排序比较两个Student对象(先按年龄比较,年龄相同按照薪资比较),使用Lambda表达式作为参数传递

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 Test1 {
@Test
public void test01() {
List<Student> stuList = Arrays.asList(
new Student("张三", 28, 4800,Course.JAVA),new Student("李四", 36, 7200,Course.JAVA),new Student("王五", 19, 9600,Course.HTML),new Student("赵六", 42, 6100,Course.HTML),new Student("孙七", 23, 9600,Course.PYTHON),new Student("吴八", 31, 3000,Course.PYTHON));

Collections.sort(stuList, (a,b)-> {
if(a.getAge() == b.getAge()){
return Double.compare(a.getSalary(),b.getSalary());
}
return a.getAge()-b.getAge();
});

for (Student stu : stuList) {
System.out.println(stu);
}
}
}
enum Course{//课程枚举
JAVA,HTML,PYTHON;
}
class Student{//学生类

private String name;
private int age;
private double salary;
private Course course;
...
}

2.创建I1接口,创建抽象方法:public String getValue(String str),在测试类中编写方法使用接口作为参数,将一个字符串转为大写,并作为方法的返回值

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Test1 {
@Test
public void test01() {
String strHandler = strHandler("abc", x-> x.toUpperCase());
System.out.println(strHandler);
}
public static String strHandler(String str,I1 i1){
return i1.getValue(str);
}
}
interface I1{
public String getValue(String str);
}

3.创建I1<T,R>接口,泛型T为参数,R为返回值,创建抽象方法:public R add(T t1,T t2),在测试类中编写方法使用接口作为参数,计算两个long类型的和

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Test1 {
@Test
public void test01() {
Long addLong = addLong(100L, 200L, (x,y)-> x+y);
System.out.println(addLong);
}
public static Long addLong(Long l1,Long l2,I1<Long,Long> i1){
return i1.add(l1, l2);
}
}
interface I1<T,R>{
public R add(T t1,T t2);
}

函数式接口

简介

函数式接口是指仅仅只包含一个抽象方法的接口,jdk1.8提供了一个@FunctionalInterface注解来定义函数式接口,如果我们定义的接口不符合函数式的规范便会报错。配合Lambda表达式一起使用

四大核心函数式接口

函数式接口参数类型返回类型用途
Consumer消费型接口Tvoidvoid accept(T t);
Supplier供给型接口voidTT get();
Function<T, R> 函数型接口TRR apply(T t);
Predicate断言型接口Tbooleanbooelan test(T t);
BiConsumer<T, U>T,Uvoid对类型为T,U参数应用操作。包含方法为void accept(T t,U u);
BiFunction<T, U, R>T,UR对类型为T,U参数应用操作,并返回R类型的结果。包含方法为R apply(T t,U u);
UnaryOperatorextends Function<T, T>TT对类型为T的对象进行一元运算,并返回T类型的结果。包含方法为T apply(T t);
BinaryOperatorextends BiFunction<T,T,T>T,TT对类型为T的对象进行二元运算,并返回T类型的结果。包含方法为T apply(T t1,T t2);
ToIntFunctionToLongFunctionToDoubleFunctionTint long double分别计算int、long、double值的函数
IntFunctionLongFunctionDoubleFunctionint long doubleR参数为int、long、double类型的函数

应用场景:当项目中需要一个接口,并且该接口中只有一个抽象方法,就没必要去创建新的接口,直接选择Java提供的使用合适的函数式接口即可

方法、构造方法和数组引用

方法、构造方法和数组引用就是Lambda的另一种表现形式

方法引用

若Lamdba表达式中的内容由方法已经实现了,可以使用方法引用这个技能

当你需要使用方法引用时,目标引用放在分隔符::前,方法的名称放在后面

对象::实例方法

Lambda表达式中调用方法的参数类型和返回值必须和函数式接口中的抽象方法一致

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Test1 {
@Test
public void test01() {
// I1 i1 = (x)->System.out.println(x);
// i1.method("abcd");

//println里的参数列表和返回值类型必须和method方法一致才行
PrintStream ps = System.out;
I1 i1 = ps::println;//对象::实例方法
i1.method("abcd");
}
}
interface I1{
public void method(String str);
}

类名::静态方法

Lambda表达式中调用方法的参数类型和返回值必须和函数式接口中的抽象方法一致

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Test1 {
@Test
public void test01() {
// Comparator<Integer> com = (x,y)-> Integer.compare(x, y);
// int compare = com.compare(10, 20);
// System.out.println(compare);

//类名::静态方法
Comparator<Integer> com = Integer::compare;
int compare = com.compare(10, 20);
System.out.println(compare);
}

}

类名::实例方法

Lambda表达式参数列表中第一个参数必须是实例方法的调用者

Lambda表达式参数列表中第二个参数必须是实例方法的参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Test1 {
@Test
public void test01() {
// I1<String> i1 = (x,y) -> x.equals(y);
// boolean method = i1.method("abc", "abc");
// System.out.println(method);

//类名::实例方法
//注意:Lambda表达式参数列表中第一个参数是equals方法的调用者,
// Lambda表达式参数列表中第二个参数是equals方法的参数
I1<String> i1 = String::equals;
boolean method = i1.method("abc", "abc");
System.out.println(method);
}
}
interface I1<T>{
public boolean method(T t1,T t2);
}

构造方法引用

类名::new

需要调用的构造方法的参数列表必须和函数式接口中抽象方法的参数列表一致

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Test1 {
@Test
public void test01() {
//需求:创建学生对象
I1<Student> i1 = Student::new;
// System.out.println(i1.method());
// System.out.println(i1.method("桥本有菜",24));
System.out.println(i1.method("桥本有菜",24,8888,Course.JAVA));
}
}
interface I1<T>{
// public T method();
public T method(String name,int age,double salary,Course course);
}
enum Course{//课程枚举
JAVA,HTML,PYTHON;
}
class Student{//学生类
private String name;
private int age;
private double salary;
private Course course;
...
}

数组引用

语法格式:type[]::new

1
2
3
4
5
6
7
8
9
10
11
public class Test1 {
@Test
public void test01() {
//创建数组
I1<String[]> i1 = String[]::new;
System.out.println(Arrays.toString(i1.method(10)));
}
}
interface I1<T>{
public T method(int capacity);
}

Stream

简介

Stream(流)是数据渠道,用于操作数据源(集合、数组等),生成元素序列。换言之,集合是存储数据的容器,流使用操作这些数据的

Stream可以对集合进行非常复杂的查找、过滤、映射数据等操作,类似于SQL执行数据库查询。Stream提供了一种高效且易于使用的处理数据的方式

注意:

  • Stream不会存储数据
  • Stream不会改变源数据,通过一系列操作数据源会返回一个持有结果的新Stream
  • Stream操作是延迟执行的,意味着流会等到需要结果的时候才执行

执行步骤

  1. 创建Stream:通过数据源(集合、数组等)获取一个Stream
  2. 中间操作:中间操作链,对源数据的数据进行处理
  3. 终止操作:执行中间操作,并产生结果

创建Stream

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 Test1 {
@Test
public void test01() {
//方式一:通过Collection接口提供的stream()-串行流或parallelStream()-并行流 获取流对象
List<String> list = new ArrayList<>();
Stream<String> stream1 = list.stream();

//方式二:通过Arrays的静态方法stream()获取流对象
String[] strs = new String[10];
Stream<String> stream2 = Arrays.stream(strs);

//方式三:通过Stream的静态方法of()获取流对象
Stream<String> stream3 = Stream.of("aaa","bbb","ccc");

//方式四:创建无限流
//iterate()迭代
Stream<Integer> stream4 = Stream.iterate(1, (x)->x+=100);
stream4.limit(3).forEach(System.out::println);
//生成
Stream<Double> stream5 = Stream.generate(()->Math.random());
stream5.limit(3).forEach(System.out::println);
}
}

注意:多个中间操作可以连接成一个流水线,除非流水线触发终止操作,否则中间操作不会执行任何的处理,而在终止操作时一次性全部处理,称为惰性求值/延迟加载

中间操作 - 筛选与切片

方法描述
filter(Predicate p)从流中排除元素
limit(long maxSize)设置限制数据条数
skip(long n)跳过元素,返回跳过n个元素的流,若流中不满足n个元素则返回空流。与limit()互补
distinct()筛选,流通过元素对象的hashCode()和equals()方法去除重复元素

如果没有终止操作,中间操作就不会被调用,终止操作时一次性全部处理,这种称为惰性求值/延迟加载

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
public class Test1 {
List<Student> stuList = Arrays.asList(
new Student("张三", 28, 4800,Course.JAVA),
new Student("李四", 36, 7200,Course.JAVA),
new Student("王五", 19, 9600,Course.HTML),
new Student("赵六", 42, 6100,Course.HTML),
new Student("孙七", 23, 9600,Course.PYTHON),
new Student("吴八", 31, 3000,Course.PYTHON),
new Student("李四", 36, 7200,Course.JAVA));
@Test
public void test01() {
//需求1:过滤掉小于5000的学生对象
Stream<Student> stream = stuList.stream().filter((x)-> {
System.out.println("中间操作");
return x.getSalary()>5000;
});
//迭代输出流里的数据就等同于终止操作
//迭代功能在forEach()中完成,称为内部迭代(集合使用iterator()称为外部迭代)
stream.forEach(System.out::println);
}
@Test
public void test02() {
//需求2:过滤掉小于5000的学生对象,并显示3条
//注意:因为限制了数据条数,所以满足数据条数后,后续的操作就不再运行了,效率就提高了
Stream<Student> stream = stuList.stream().filter((x)-> {
System.out.println("短路");
return x.getSalary()>5000;
}).limit(3);
//迭代输出流里的数据就等同于终止操作
//迭代功能在forEach()中完成,称为内部迭代(集合使用iterator()称为外部迭代)
stream.forEach(System.out::println);
}
@Test
public void test03() {
//需求3:过滤掉小于5000的学生对象,并跳过第1个学生对象
Stream<Student> stream = stuList.stream().
filter((x)-> x.getSalary()>5000).
skip(1);
//迭代输出流里的数据就等同于终止操作
//迭代功能在forEach()中完成,称为内部迭代(集合使用iterator()称为外部迭代)
stream.forEach(System.out::println);
}
@Test
public void test04() {
//需求4:过滤掉小于5000的学生对象,并筛选掉重复元素
//Stream底层通过元素对象(Student对象)的hashCode()和equals()方法去除重复元素
Stream<Student> stream = stuList.stream().
filter((x)-> x.getSalary()>5000).
distinct();

//迭代输出流里的数据就等同于终止操作
//迭代功能在forEach()中完成,称为内部迭代(集合使用iterator()称为外部迭代)
stream.forEach(System.out::println);
}
}
enum Course{//课程枚举
JAVA,HTML,PYTHON;
}
class Student{//学生类
private String name;
private int age;
private double salary;
private Course course;
...
}

中间操作 - 映射

方法描述
map(Function<?, ? > mapper)将流中所有元素映射成一个新的元素或者提取信息
flatMap(Function<?, ? extends Stream<? >> mapper)将流中的流整合(整合成平面/平铺流)
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
public class Test1 {

List<String> nameList = Arrays.asList("张三","李四","王五","赵六","孙七","吴八");
List<Student> stuList = Arrays.asList(
new Student("张三", 28, 4800,Course.JAVA),
new Student("李四", 36, 7200,Course.JAVA),
new Student("王五", 19, 9600,Course.HTML),
new Student("赵六", 42, 6100,Course.HTML),
new Student("孙七", 23, 9600,Course.PYTHON),
new Student("吴八", 31, 3000,Course.PYTHON),
new Student("李四", 36, 7200,Course.JAVA));
@Test
public void test01() {
//map() - 将流中所有元素映射成一个新的元素 或者 提取信息

//方式1:映射成一个新的元素
//需求://需求:nameList获取流对象,打印出所有学生的姓氏
nameList.stream().map((str)-> str.charAt(0)).forEach(System.out::println);

//方式2:映射成提取信息
//需求:把原来流中的学生对象替换成学生姓名
stuList.stream().map((stu)-> stu.getName()).forEach(System.out::println);
}
@Test
public void test02() {
//带着需求学flatMap()
//flatMap() - 将流中的流整合(整合成平面/平铺流)
//需求:将nameList里的字符串转换为字符输出

//解决方案1:使用map()完成需求,可以看到流里包含另外的流,非常麻烦
Stream<Stream<Character>> stream = nameList.stream().
map(Test1::getCharacterStream);//{{'张','三'},{'李','四'},...}
stream.forEach((sm) -> {
sm.forEach(System.out::println);
});

//解决方案2:使用flatMap()完成需求,将流中的流一并整合
nameList.stream().flatMap((str)-> getCharacterStream(str)).
forEach(System.out::println);//{'张','三'},{'李','四'},...
}
//将字符串拆分出字符转换为流的方法
public static Stream<Character> getCharacterStream(String str){
ArrayList<Character> list = new ArrayList<>();

for (Character c : str.toCharArray()) {
list.add(c);
}
return list.stream();
}
}
enum Course{//课程枚举
JAVA,HTML,PYTHON;
}
class Student{//学生类
private String name;
private int age;
private double salary;
private Course course;
...
}

中间操作 - 排序

方法解释
sorted()使用元素原有排序规则 - Comparable
sorted(Comparator<? super T> comparator)使用自定义排序规则 - Comparator
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 Test1 {
List<Student> stuList = Arrays.asList(
new Student("张三", 28, 4800,Course.JAVA),
new Student("李四", 36, 7200,Course.JAVA),
new Student("王五", 19, 9600,Course.HTML),
new Student("赵六", 42, 6100,Course.HTML),
new Student("孙七", 23, 9600,Course.PYTHON),
new Student("吴八", 31, 3000,Course.PYTHON),
new Student("吴八", 31, 3000,Course.PYTHON));
@Test
public void test01() {
//使用元素原有排序规则(Comparable<T>)
//需求:按照年龄排序
stuList.stream().sorted().forEach(System.out::println);
}
@Test
public void test02() {
//使用自定义排序规则(Comparator<T>)
//需求:按照工资排序
stuList.stream().sorted((e1,e2)->{
if(e1.getSalary() == e2.getSalary()){
return 1;
}
return (int)(e1.getSalary() - e2.getSalary());
}).forEach(System.out::println);
}
}
enum Course{//课程枚举
JAVA,HTML,PYTHON;
}
class Student implements Comparable<Student>{//学生类
private String name;
private int age;
private double salary;
...
}

终止操作 - 匹配与查找

方法描述
allMatch(Predicate<? super T> predicate)检查是否匹配所有元素
anyMatch(Predicate<? super T> predicate)检查是否匹配至少一个元素
noneMatch(Predicate<? super T> predicate)检查是否没有匹配所有元素
findFirst()返回第一个元素
findAny()返回任意一个元素(但效果不好)
count()返回流中元素的个数
max(Comparator<? super T> comparator)返回流中最大值
min(Comparator<? super T> comparator)返回流中最小值
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
public class Test1 {
List<Student> stuList = Arrays.asList(
new Student("张三", 28, 4800,Course.JAVA),
new Student("李四", 36, 7200,Course.JAVA),
new Student("王五", 19, 9600,Course.HTML),
new Student("赵六", 42, 6100,Course.HTML),
new Student("孙七", 23, 9600,Course.PYTHON),
new Student("吴八", 31, 3000,Course.PYTHON),
new Student("吴八", 31, 3000,Course.PYTHON));
@Test
public void test01() {
//需求1:检查流中素所有元素是否匹配 工资>5000
boolean allMatch = stuList.stream().allMatch((stu) -> stu.getSalary()>5000);
System.out.println(allMatch);

//需求2:检查流中素所有元素至少匹配一个 工资>5000
boolean anyMatch = stuList.stream().anyMatch((stu) -> stu.getSalary()>5000);
System.out.println(anyMatch);

//需求3:检查流中素所有元素是否没有匹配 工资>5000
boolean noneMatch = stuList.stream().noneMatch((stu) ->
stu.getSalary()>5000);
System.out.println(noneMatch);

//需求3:返回工资最高的学生信息
Optional<Student> findFirst = stuList.stream().
sorted((stu1,stu2)->Double.compare(
stu1.getSalary(),stu2.getSalary())).
findFirst();
Student stu = findFirst.get();
//这种写法防止NullPointerException出现
//Student stu = findFirst.orElse(new Student());
System.out.println(stu);

//需求4:返回随机学生信息(但效果不好)
Optional<Student> findAny = stuList.stream().findAny();
Student stu = findAny.get();
System.out.println(stu);

//需求5:获取学生个数
long count = stuList.stream().count();
System.out.println(count);

//需求6:获取最高工资的学生信息
Optional<Student> max = stuList.stream().
max((stu1,stu2)->Double.compare(stu1.getSalary(),stu2.getSalary()));
System.out.println(max.get());

//需求7:获取最工资的学生信息
Optional<Student> min = stuList.stream().
min((stu1,stu2)->Double.compare(stu1.getSalary(),stu2.getSalary()));
System.out.println(min.get());
}
}
enum Course{//课程枚举
JAVA,HTML,PYTHON;
}
class Student implements Comparable<Student>{//学生类
private String name;
private int age;
private double salary;
private Course course;
...
}

终止操作 - 归约

归约:将流中的元素反复结合起来,得到一个值

map+reduce的连接通常称为map_reduce模式,因Google用它进行网络搜索而出名

方法描述
reduce( T identity , BinaryOperatoraccumulator)参数:(初始值,结合逻辑)
reduce(BinaryOperatoraccumulator)参数:(结合逻辑)
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
public class Test1 {

List<Integer> numList = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
List<Student> stuList = Arrays.asList(
new Student("张三", 28, 4800,Course.JAVA),
new Student("李四", 36, 7200,Course.JAVA),
new Student("王五", 19, 9600,Course.HTML),
new Student("赵六", 42, 6100,Course.HTML),
new Student("孙七", 23, 9600,Course.PYTHON),
new Student("吴八", 31, 3000,Course.PYTHON),
new Student("李四", 36, 7200,Course.JAVA));
@Test
public void test01() {
//需求:获取numList集合中元素的总和
Integer reduce = numList.stream().
reduce(0, (x,y)->x+y);
System.out.println(reduce);
}
@Test
public void test02() {
//需求:获取stuList集合中所有学生工资总和
Optional<Double> reduce = stuList.stream().
map(Student::getSalary).reduce(Double::sum);
Double sumSalary = reduce.get();
System.out.println(sumSalary);
}
}
enum Course{//课程枚举
JAVA,HTML,PYTHON;
}
class Student implements Comparable<Student>{//学生类
private String name;
private int age;
private double salary;
private Course course;

终止操作 - 收集

收集:将流转换为其他形式。接收一个Collector接口的实现,用于给Stream中元素做汇总的方法

方法描述
collect(Collector<? super T, A, R> collector)把元素放入Collector集合中
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
public class Test1 {

List<Student> stuList = Arrays.asList(
new Student("张三", 28, 4800,Course.JAVA),
new Student("李四", 36, 7200,Course.JAVA),
new Student("王五", 19, 9600,Course.HTML),
new Student("赵六", 42, 6100,Course.HTML),
new Student("孙七", 23, 9600,Course.PYTHON),
new Student("吴八", 31, 3000,Course.PYTHON),
new Student("李四", 36, 7200,Course.JAVA));

@Test
public void test01() {
//把数据收集到集合中

//需求1:把当前学生姓名提取出来,并把数据放入List集合中
List<String> list = stuList.stream().
map(Student::getName).
collect(Collectors.toList());
list.forEach(System.out::println);

//需求2:把当前学生姓名提取出来,并把数据放入Set集合中
Set<String> set = stuList.stream().
map(Student::getName).collect(Collectors.toSet());
set.forEach(System.out::println);

//需求3:把当前学生姓名提取出来,并把数据放入指定集合中
HashSet<String> hashSet = stuList.stream().
map(Student::getName).collect(Collectors.toCollection(HashSet::new));
hashSet.forEach(System.out::println);
}
@Test
public void test02() {
//收集流中的各种数据

//需求1:收集/获取学生个数
Long count = stuList.stream().
map(Student::getName).collect(Collectors.counting());
System.out.println(count);

//需求2:收集/获取学生平均工资
Double avg = stuList.stream().
collect(Collectors.averagingDouble(Student::getSalary));
System.out.println(avg);

//需求3:收集/获取学生总工资
Double sum = stuList.stream().
collect(Collectors.summingDouble(Student::getSalary));
System.out.println(sum);

//需求4:收集/获取学生工资最大值
Optional<Double> max = stuList.stream().map(Student::getSalary).
collect(Collectors.maxBy(Double::compareTo));
System.out.println(max.get());

//需求5:收集/获取学生工资最小值
Optional<Double> min = stuList.stream().map(Student::getSalary).
collect(Collectors.minBy(Double::compareTo));
System.out.println(min.get());

//需求6:收集/获取工资最多的学生信息
Optional<Student> maxStu = stuList.stream().
collect(Collectors.maxBy(
(stu1,stu2)-> (int)(stu1.getSalary()-stu2.getSalary())));
System.out.println(maxStu.get());

//需求7:收集/获取工资最多的学生信息
Optional<Student> minStu = stuList.stream().
collect(Collectors.maxBy(
(stu1,stu2)-> -(int)(stu1.getSalary()-stu2.getSalary())));
System.out.println(minStu.get());
}
@Test
public void test03() {//分组
//需求:按照学科分组
Map<Course, List<Student>> map = stuList.stream().collect(
Collectors.groupingBy(Student::getCourse));
System.out.println(map);
}
@Test
public void test04() {//多级分组
//需求:按照学科分组,在按照年龄分组
Map<Course, Map<String, List<Student>>> map = stuList.stream().
collect(Collectors.groupingBy(
Student::getCourse,Collectors.groupingBy((stu)->{
if(((Student)stu).getAge() < 28){
return "青年";
}else if(((Student)stu).getAge() < 40){
return "中年";
}else{
return "老年";
}
})));
System.out.println(map);
}
@Test
public void test05() {//分区
//需求:按照工资5000为标准分区
Map<Boolean, List<Student>> map = stuList.stream().collect(
Collectors.partitioningBy((stu) -> stu.getSalary()>5000));
System.out.println(map);
}
@Test
public void test06() {//获取元素中字段的各种信息
//需求:获取学生工资信息,再获取总值、平均值、最大值、最小值
DoubleSummaryStatistics collect = stuList.stream().collect(
Collectors.summarizingDouble(Student::getSalary));
System.out.println(collect.getSum());
System.out.println(collect.getAverage());
System.out.println(collect.getMax());
System.out.println(collect.getMin());
}
@Test
public void test07() {//拼接信息
//需求:拼接学生姓名
String str1 = stuList.stream().map(Student::getName).collect(
Collectors.joining());
System.out.println(str1);

String str2 = stuList.stream().map(Student::getName).collect(
Collectors.joining(","));
System.out.println(str2);

String str3 = stuList.stream().map(Student::getName).collect(
Collectors.joining(",","--","--"));
System.out.println(str3);
}
}
enum Course{//课程枚举
JAVA,HTML,PYTHON;
}
class Student implements Comparable<Student>{//学生类
private String name;
private int age;
private double salary;
private Course course;
...
}

并行流与串行流

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

注意

  1. 默认为顺序流/串行流
  2. 并行流一般在大数据搜索里使用到
  3. JDK1.8之前也有并行流,叫做Fork/Join并行计算框架
1
2
3
4
5
6
7
8
9
10
11
public class Test1 {
@Test
public void test01() {
//需求:使用并行流计算1-10000000L之和
OptionalLong reduce = LongStream.
range(0, 10000001L).//生成1-10000000的数流
parallel(). //转换为并行流
reduce(Long::sum); //求和
System.out.println(reduce);
}
}

Optional

Optional类(java. util. Optional)是一个容器类,代表一个存在或不存在的值,原来用null表示一个值不

存在,现在Optional可以更好的表达这个概念。并且可以避免空指针异常

此类的设计就是更好的避免空指针异常

方法描述
Optional.of(T t)创建一个Optional实例
Optional.empty()创建一 个空的 Optional实例
Optional.ofNullable(T t)若t不为null,创建Optional实例,否则创建空实例
get()获取Optional实例里的对象
isPresent()判断是否包含值
orElse(T t)如果调用对象包含值, 返回该值,否则返回t
orElseGet(Supplier s)如果调用对象包含值,返回该值,否则返回s获取的值
map(Function f)如果有值对其处理,并返回处理后的Optional,否则返回optional. empty()
flatMap(Function mapper)与map 类似,要求返回值必须是Optional
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
public class Test1 {
@Test
public void test01() {
//创建一个Optional实例,把对象封装到Optional容器里
// Optional<Student> op = Optional.of(
// new Student("aaa", 26, 6666, Course.HTML));
//创建一个空的Optional容器
// Optional<Student> op = Optional.empty();
//创建一个Optional实例,若对象为null->创建空实例,若对象为不为null->创建Optional实例
Optional<Student> op = Optional.ofNullable(
new Student("bbb", 26, 7777, Course.PYTHON));

//判断容器里是否包含对象
if(op.isPresent()){//包含
System.out.println("获取容器里的对象:" + op.get().getName());
}else{//不包含
System.out.println("容器里的对象为null");
}

//如果容器里有对象就返回,否则就返回新对象
Student stu = op.orElse(new Student("ccc", 26, 8888, Course.JAVA));
System.out.println(stu.getName());

//不同情况下可以返回不同对象,orElseGet()比orElse()可变性更强
boolean bool = true;
stu = op.orElseGet(()->{
if(bool){
return new Student("吴彦祖", 26, 8888, Course.JAVA);
}else{
return new Student("麻生希", 26, 8888, Course.JAVA);
}
});

//获取原容器中的某个值并返回新容器中
//map(Function<? super T, ? extends U> mapper)
Optional<String> map = op.map(Student::getName);
System.out.println(map.get());

//与map 类似,要求返回值必须是Optional
//flatMap(Function<? super T, Optional<U>> mapper)
Optional<String> flatMap = op.flatMap((e)->Optional.of(e.getName()));
System.out.println(flatMap.get());
}
}
enum Course{//课程枚举
JAVA,HTML,PYTHON;
}
class Student implements Comparable<Student>{//学生类
private String name;
private int age;
private double salary;
private Course course;
...
}

接口的默认方法与静态方法

从JDK1.8开始,接口中可以有默认方法,既default修饰的方法,此方法可以让接口的实现类所调用,而接

口中的静态方法直接用接口名调用即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Test1 {
@Test
public void test01() {
MyClass myClass = new MyClass();
myClass.defaultMethod();

I1.staticMethod();
}
}
interface I1{
default void defaultMethod(){
System.out.println("接口中的默认方法");
}
public static void staticMethod(){
System.out.println("接口中的静态方法");
}
}
class MyClass implements I1{}

接口默认方法的”类优先”原则:

  • 如果一个接口中定义了一个默认方法,而接口实现类的父类定义了一个同名的方法时,选择父类中的方法

  • 接口冲突:如果一个父接口提供一个默认方法,而另一个接口也提供了一个具有相同名称和参数列表的方法(不管方法是否是默认方法),那么必须覆盖该方法来解决冲突

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Test1 {
@Test
public void test01() {
MyClass myClass = new MyClass();
MyClass.method();
}
}
interface I1{
default void method(){
System.out.println("I1接口中的默认方法");
}
}
class Father {
public void method(){
System.out.println("Father类中的默认方法");
}
}
class MyClass extends Father implements I1 {}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Test1 {
@Test
public void test01() {
MyClass myClass = new MyClass();
myClass.method();
}
}
interface I1{
default void method(){
System.out.println("I1接口中的默认方法");
}
}
interface I2{
default void method(){
System.out.println("I2接口中的默认方法");
}
}
class MyClass implements I1,I2 {
@Override
public void method() {
//I1.super.method();
I2.super.method();
}
}

日期组件

JDK1.8提供的新日期类都是不可变的,既不管怎么样的改变,都会产生一个新的实例,他们都是线程安全的

日期组件遵循与IOS-8601世界时间标准

组件简介

包路径类名描述
java.time针对日期和时间操作的包
LocalDate用于表示日期的类
LocalTime用于表示时间的类
LocalDateTime用于表示日期时间的类
Instant时间戳类(1970.1.1 0:0:0 到线程的毫秒数)
Period两个日期间隔类
Duration两个时间间隔类
java.time.chrono针对日期时间特殊格式操作的包
JapaneseChronology日本帝国历法系统类
ThaiBuddhistChronology泰国佛教日历系统类
java.time.format针对时间日期时间格式化操作的包
DateTimeFormatter格式化日期时间类
java.time.temporal针对时间矫正操作的包
java.time.zone针对时区操作的包

日期时间类、时间戳、间隔类

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
public class Test1 {
@Test
public void test01() {
//LocalDate LocalTime LocalDateTime
//这三个日期类的使用大致一样

//获取当前日期时间对象
LocalDateTime ldt1 = LocalDateTime.now();
System.out.println(ldt1);

//获取指定日期时间对象
LocalDateTime ldt2 = LocalDateTime.of(2020, 1, 23, 8, 30, 10, 10);
System.out.println(ldt2);

//获取ldt1推后的时间日期对象
LocalDateTime ldt3 = ldt1.plusYears(2);
System.out.println(ldt3);

//获取ldt1提前的时间日期对象
LocalDateTime ldt4 = ldt3.minusMonths(2);

System.out.println(ldt4.getYear());
System.out.println(ldt4.getMonthValue());
System.out.println(ldt4.getDayOfMonth());
System.out.println(ldt4.getHour());
System.out.println(ldt4.getMinute());
System.out.println(ldt4.getSecond());
}
@Test
public void test02() {
//使用时间戳(从1970年1月1日0:0:0到现在的毫秒值)

//默认创建UTC(世界标准时间)时区的时间戳对象
Instant now1 = Instant.now();
System.out.println(now1);

//获取偏移8小时的偏移日期时间对象
OffsetDateTime odt = now1.atOffset(ZoneOffset.ofHours(8));
System.out.println(odt);

//获取时间戳的毫秒值形式
System.out.println(now1.toEpochMilli());

//获取一个1970年1月1日0:0:0 往后退1秒的时间戳对象
Instant now2 = Instant.ofEpochSecond(1);
System.out.println(now2);
}
@Test
public void test03() throws InterruptedException {
//Duration:时间间隔类

Instant now1 = Instant.now();
Thread.sleep(1000);
Instant now2 = Instant.now();
//获取时间间隔类对象
Duration duration1 = Duration.between(now1, now2);
System.out.println(duration1.toMillis());

System.out.println("-----------------------------");

LocalTime lt1 = LocalTime.now();
Thread.sleep(1000);
LocalTime lt2 = LocalTime.now();
//获取时间间隔类对象
Duration duration2 = Duration.between(lt1, lt2);
System.out.println(duration2.toMillis());
}
@Test
public void test04() throws InterruptedException {
//Period:日期间隔类

LocalDate ld1 = LocalDate.now();
Thread.sleep(1000);
LocalDate ld2 = LocalDate.of(2020, 12, 31);

Period period = Period.between(ld1, ld2);
System.out.println(period.getYears());
System.out.println(period.getMonths());
System.out.println(period.getDays());
}
}

日期时间格式化类-DateTimeFormatter

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 Test1 {
@Test
public void test01() {
//格式化日期时间类

LocalDateTime ldt1 = LocalDateTime.now();

//获取本地标准的日期时间格式化对象
DateTimeFormatter dtf1 = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
String strDateTime1 = ldt1.format(dtf1);//格式化时间日期
System.out.println(strDateTime1);

//自定义日期时间格式化对象
DateTimeFormatter dtf2 = DateTimeFormatter.
ofPattern("yyyy年MM月dd日 HH:mm:ss");
String strDateTime2 = ldt1.format(dtf2);//格式化时间日期
System.out.println(strDateTime2);

//将指定格式的字符串解析成LocalDateTime对象
LocalDateTime parse = LocalDateTime.parse("2020年03月12日 11:04:14", dtf2);
System.out.println(parse);
}
}

时间矫正器类-TemporalAdjuster

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
public class Test1 {
@Test
public void test01() {
//时间矫正器

LocalDateTime ldt1 = LocalDateTime.now();

//设置指定月份
LocalDateTime ldt2 = ldt1.withMonth(10);
System.out.println(ldt2);

//设置下一个周末
LocalDateTime ldt3 = ldt1.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
System.out.println(ldt3);

//自定义时间矫正器:设置下一个工作
LocalDateTime ldt4 = ldt1.with((temporal)->{
LocalDateTime ldt = (LocalDateTime) temporal;
DayOfWeek week = ldt.getDayOfWeek();
if(week.equals(DayOfWeek.FRIDAY)){//周五
return ldt.plusDays(3);
}else if(week.equals(DayOfWeek.SATURDAY)){//周六
return ldt.plusDays(2);
}else{
return ldt.plusDays(1);
}
});
System.out.println(ldt4);
}
}

时区类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Test1 {
@Test
public void test01() {
//时区类

//获取所有时区字符串
Set<String> set = ZoneId.getAvailableZoneIds();
for (String str : set) {
System.out.println(str);
}

//获取指定时区的日期时间对象
LocalDateTime ldt1 = LocalDateTime.now(ZoneId.of("Asia/Tokyo"));
System.out.println(ldt1);

//获取指定时区的日期时间对象 + 偏移量
LocalDateTime ldt2 = LocalDateTime.now(ZoneId.of("Asia/Tokyo"));
ZonedDateTime zonedDateTime = ldt2.atZone(ZoneId.of("Asia/Tokyo"));
System.out.println(zonedDateTime);
}
}

重复注解及类型注解

jdk1.8开始可以重复注解

ps:一个类可有多个同样的注解

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
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.TYPE_PARAMETER;

import java.lang.annotation.Annotation;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;

import org.junit.Test;

//重复注解
@Author(name="何老师")
@Author(name="苍老师")
public class Test1 {
@Test
public void test01() throws Exception{

//需求1:获取Test1的多个作者注解
Class<Test1> ct = Test1.class;
Author[] as = ct.getAnnotationsByType(Author.class);
for (Author a : as) {
System.out.println(a.name());
}

//需求2:获取method方法中参数里的注解
Method m = ct.getMethod("method", String.class);
Annotation[][] parameterAnnotations = m.getParameterAnnotations();
//获取参数中注解列表(有可能一个参数多个注解)
for (Annotation[] annotations : parameterAnnotations) {
for (Annotation an : annotations) {//获取具体注解
System.out.println(an);
}
}
}

public static void method(@Author(name="波老师") String str){}
}

//作者注解的容器
@Target(TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Authors{
Author[] value();
}
//作者注解
@Repeatable(Authors.class)
@Target({TYPE, PARAMETER,TYPE_PARAMETER})//TYPE_PARAMETER-类型注解:作用在参数
@Retention(RetentionPolicy.RUNTIME)
@interface Author{
String name();
}