java8新特性

Lambda表达式

Lambda是一个匿名函数。

直接给例子。

1
2
3
4
5
public void test1(){
Runnable r1 = () -> System.out.println(111);
Comparator<Integer> com = (o1,o2) -> Integer.compare(o1,o2);
Comparator<Integer> com2 = Integer :: compare;
}

但是一开始看不懂下面会详细讲解一下

举例 (o1,o2) -> Integer.compare(o1,o2);

格式:

这个箭头 -> 称为箭头操作符

箭头的左边是形参列表(其实就是接口中的抽象方法的 形参列表)

箭头的右边:Lambda体(其实就是重写的抽象方法的方法体)

Lambda表达式本质:作为接口的实例

使用(分为6种情况)

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
Runnable r1 = new Runnable(){
@Override
public void run(){
System.out.println("111");
}
};
//方法1:无参,无返回值
Runnable r2 = () -> {
System.out.println("1111");
}

//方法2: 需要一个参数但是没有返回值
Consumer<String> con = new Consumer<String>(){
@Override
public void accept(String s){
System.out.println();
}
};
Consumer<String> con1 = (String s) -> {
System.out.println(s);
}

//方法3:数据类型可以省略,因为可以由编译器推断得出,称为“类型推断”
Consumer<String> con2 = (s) -> {
System.out.println(s);
}

//方法4: Lambda若只需要一个参数的时候,参数的小括号可以省略
Consumer<String> con2 = s -> {
System.out.println(s);
}
//方法5:Lambda需要两个或以上的参数,多条执行语句,并且可以有返回值
Comparator<Integer> com1 = new Comparator<Integer>(){
@Override
public int compare(Integer o1,Integer o2){
System.out.println(o1);
return o1.compareTo(o2);
}
};
Comparator<Integer> com2 = (o1,o2) -> {
System.out.println(o1);
return o1.compareTo(o2);
};
//方法6:当lambda体只有一条语句的时候,return与大括号若有,都可以省略
Comparator<Integer> com2 = (o1,o2) -> o1.compareTo(o2);

总结

箭头左边:形参列表的参数类型可以省略(类型推断),如果形参列表只有一个参数,()也可以省略

箭头右边:方法体应该使用一对{}包裹,如果方法体或者返回语句只有一行,可以省略return 和{}

本质:作为接口的实例,并且只有一个抽象方法

函数式(Funtional)接口

如果一个接口中,只有一个抽象方法那么就称为函数式接口。

1
2
3
4
5
@FuntionalInterface
public interface MyInterface{
void print();
}
//这个注解用来检验是否是函数式接口

JAVA内置的四大核心函数式接口

image-20250302180637330

1
2
3
4
5
6
7
8
9
10
11
public class test {

@Test
public void test() {
happyTime(500, (money) -> System.out.println("花了" + money + "元"));
}

public void happyTime(double money, Consumer<Double> con){
con.accept(money);
}
}

方法引用

当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用。

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//方法引用本质上是Lambda表达式,所以方法引用也是函数式接口的实例
//使用格式 类(或对象)::方法名
//具体三种情况
// 对象:: 非静态方法
// 类 :: 静态方法
// 类 :: 非静态方法
//具体要求,对象和方法的形参列表和返回值类型相同才可以(适用情况1和2)
//情况1 : 对象::实例方法
//Consumer中的void accept(T t)
//PrintStream中的void println(T t)

Consumer<String> con1 = str -> System.out.println(str);
Consumer<String> con1 = System.out::println

image-20250302185515580

image-20250302190009034

image-20250302190357666

image-20250302190841085

image-20250302191130786

image-20250302191349747

构造器引用

函数式接口的抽象方法的形参列表和构造器的形参列表一致。

抽象方法的返回值类型即为构造器所属的类的类型

image-20250302191736019

image-20250302192144429

强大的Stram API

Stream是java8中处理集合的关键抽象概念。

使用StreamAPI对集合数据进行操作,类似于SQL执行的数据库查询。

Stream关注的是操作容器的

Stream的操作分三个步骤

1、创建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
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//方式一:通过集合(在集合里面有两个默认的方法返回顺序流和并行流)
@Test
public void test1(){
List<Employee> employees = EmployeeData.getEmployees();
//在List里面默认有个stream方法,返回一个顺序流
Stream<Employee> stream = employees.stream();

//返回的一个并行流
Stream<Employee> parallelStream = employees.parallelStream();

}
//方式二:通过数组(在Arrays工具类里面有一个静态方法)
@Test
public void test2(){
int[] arr = new int[]{1,2,3};
IntStream stream = Arrays.stream(arr);

Employee e1 = new Employee();
Employee e2 = new Employee();
Employee[] arr1 = new Employee[]{e1,e2};
Stream<Employee> stream1 = Arrays.stream(arr1);

}
//方式三:通过Stream的of()
@Test
public void test2(){
Stream<Integer> stream = Stream.of(1,2,3,4,5,6);

}
//方式四:通过创建无限流
@Test
public void test2(){
//通过迭代
//遍历前10个偶数(第一个是种子(起始值),第二个是函数)
Stream.iterate(0,t -> t + 2).limit(10).forEach(System.out::println);

//生成
Stream.generate(Math::random).limit(10).forEach(System.out::println);;
}

Stream的中间操作

中间操作可以连接成一个流水线!

1、筛选与切片

image-20250302224920243

image-20250302224906784

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Test
public void test1(){
List<Employee> list = EmployeeData.getEmployees();
//通过filter过滤数据,首先调用stream方法调用stream
//filter里面传入一个参数返回的是布尔类型的,这里用Lambda表达式来写
list.stream().filter(e -> e.getSalary() > 700).foreEach(System.out::println);

//limit(n),使其元素不超过给定数量
list.stream().limit(3).foreEach(System.out::println);

//skip(n),跳过前面几个数据,若元素不足n个,则返回一个空流,啥也没有
//distinct()筛选,通过元素生成的hashCode()和equals()去除重复元素,
//如果是自定义的元素需要自己重写这两个方法才能使用distinct()
}

2、映射

image-20250302231052705

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
List<Streing> list = Arrays.asList("aa","bb");
list.stream.map(s -> s.toUpperCase()).forEach(System.out::println);

//练习:找到员工姓名长度大于3的员工的姓名
List<Employee> list = EmployeeData.getEmployees();
list.stream()
.map(e -> e.getName())//获得全是名字的映射然后再过滤
.filter(name -> name.length() > 3)
.forEach(System.out::println);
//这里的List是{"aa","bb"}
Stream<Stream<Character>> stringStream = list.stream().map(StreamAPITest1::fromStringToStream);
//这里类似于嵌套的for循环
stringStream.forEach(s -> {
s.forEach(System.out::println);
})

//flatMap类似于addAll,map类似与add
public Stream<Character> fromStringToStream(String str){
ArrayList<Character> list = new ArrayList<>();
for(Character c : str.toCharArray()){
list.add(c);
}
return list.stream();
}

3、排序

1
2
List list = Arrays.asList(12,15,12,1223);
list.stream().sorted().forEach(System.out::println);

image-20250303005414820

终止操作

1、匹配与查找

image-20250303005559684

image-20250303005732601

2、归约(归成一个)

image-20250303013534822

1
2
3
4
5
//计算1-10的和,第一个参数是起始值
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
list.stream().reduce(0,Integer::sum);


3、收集

image-20250303014537749

Collector里面有很多静态方法给你调用。具体可以查文档使用。

Optional类

Option 类是一个容器类,它可以保存类型T的值,代表这个值存在。或者仅仅保存null,表示这个值不存在。原来用null表示一个值不存在,现在Optional可以更好的表达这个概念。并且可以避免空指针异常。

image-20250303015114294

在没有这个类之前,需要多层if判断是否为空,实际中可以利用这个类来进行空值判断。