Java8新特性

动态代理

代理设计模式的原理

使用一个代理将对象包装起来,然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。

静态代理与动态代理

  • 静态代理:代理类和目标对象的类在编译期间确定,不利于程序扩展,且每个代理类只能为一个接口服务。
  • 动态代理:在程序运行时根据需要动态创建目标类的代理对象,更加灵活和统一。

动态代理的优点

动态代理相比静态代理,将抽象角色中声明的所有方法转移到调用处理器的一个集中方法中处理,可以更灵活和统一地处理众多方法。

演示静态代理(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
// 演示静态代理
interface ClothFactory {
void produceCloth();
}

// 代理类
class ProxyCloth implements ClothFactory {
private ClothFactory clothFactory; // 用被代理类对象进行实例化

public ProxyCloth(ClothFactory clothFactory) {
this.clothFactory = clothFactory;
}

@Override
public void produceCloth() {
System.out.println("代理工厂做一些准备工作...");
clothFactory.produceCloth();
System.out.println("代理工厂做一些收尾工作...");
}
}

// 被代理类
class NikeClothFactory implements ClothFactory {
@Override
public void produceCloth() {
System.out.println("nike工厂正在生产一批衣服.....");
}
}

public class StaticProxy {
public static void main(String[] args) {
// 创建被代理类对象
ClothFactory nikeClothFactory = new NikeClothFactory();
// 代理类对象
ClothFactory proxyCloth = new ProxyCloth(nikeClothFactory);
proxyCloth.produceCloth();
}
}

演示动态代理(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
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

interface Human {
String getBelief();
void eat(String food);
}

// 被代理类
class SuperMan implements Human {
@Override
public String getBelief() {
return "I am superMan!";
}

@Override
public void eat(String food) {
System.out.println("我喜欢吃" + food);
}
}

// 创建一个代理类工厂,动态地创建代理类
class ProxyFactory {
// 调用此方法,可以创建代理类对象
public static Object getProxyInstance(Object obj) { // obj 就是被代理类对象
MyInvocationHandler myInvocationHandler = new MyInvocationHandler(obj);
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), myInvocationHandler);
}
}

class MyInvocationHandler implements InvocationHandler {
private Object obj; // 创建被代理类对象

public MyInvocationHandler(Object obj) { // 使用构造函数为 被代理类对象赋值
this.obj = obj;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// method 即为代理类对象要调用的方法,此方法也作为了被代理类对象要调用的方法
// obj就是被代理的对象
Object returnValue = method.invoke(obj, args);
// 上述方法的返回值就作为当前类中的invoke()方法的返回值
return returnValue;
}
}

public class ProxyTest {
public static void main(String[] args) {
SuperMan superMan = new SuperMan();
// 动态的创建代理类对象
Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);
// 通过代理类对象调用被代理类的方法
System.out.println(proxyInstance.getBelief());
proxyInstance.eat("草莓仙草冻");
}
}

动态代理与AOP

动态代理是实现AOP(面向切面编程)的核心技术之一,通过动态代理可以在不修改原始代码的情况下,为对象添加额外的功能。


Java 8新特性

Java 8新特性概述

  • 速度更快:性能提升。
  • 代码更少:增加了新的语法,如Lambda表达式。
  • 强大的Stream API:便于数据处理。
  • 便于并行:支持并行操作。
  • 最大化减少空指针异常:引入Optional类。
  • Nashorn引擎:允许在JVM上运行JS应用。

Lambda表达式

什么是Lambda表达式

Lambda是一个匿名函数,可以将代码像数据一样传递,使Java的表达能力更强。

Lambda表达式的使用

  • 格式:(o1, o2) -> Integer.compare(o1, o2);
  • ->左边:Lambda形参列表(接口中的抽象方法的形参列表)。
  • ->右边:Lambda体(重写的抽象方法的方法体)。

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
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
// 语法格式一:无参无返回值
@Test
public void test1() {
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("我爱北京天安门");
}
};
runnable.run();
System.out.println("---------------------------");

// Lambda表达式写法
Runnable r2 = () -> {
System.out.println("我爱北京天安门.");
};
r2.run();
}

// 语法格式二:Lambda需要一个参数,但没有返回值
@Test
public void test2() {
Consumer<String> con = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
con.accept("谎言和誓言的区别是什么?");
System.out.println("-------------------------");
// Lambda表达式写法
Consumer<String> con2 = (String s) -> {
System.out.println(s);
};
con2.accept("一个是听的人信了,一个是说的人信了");
}

// 语法格式三:数据类型可以省略,因为可以由编译器推断得出,成为“类型推断”
@Test
public void test3() {
Consumer<String> con1 = (String s) -> {
System.out.println(s);
};
con1.accept("一个是听的人信了,一个是说的人信了");
System.out.println("----------------");
// 省略类型
Consumer<String> con2 = (s) -> {
System.out.println(s);
};
con2.accept("一个是听的人信了,一个是说的人信了");
}

// 语法格式四:Lambda若只需要一个参数时,参数的小括号可以省略
@Test
public void test4() {
// 省略小括号
Consumer<String> con2 = s -> {
System.out.println(s);
};
con2.accept("一个是听的人信了,一个是说的人信了");
}

// 语法格式五:Lambda需要两个或以上的参数,多条执行语句,并且可以有返回值
@Test
public void test5() {
Comparator<Integer> com = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
System.out.println(o1);
System.out.println(o2);
return o1.compareTo(o2);
}
};
System.out.println(com.compare(12, 21));
System.out.println("--------------------------");
// Lambda写法
Comparator<Integer> com2 = (o1, o2) -> {
System.out.println(o1);
System.out.println(o2);
return o1.compareTo(o2);
};
System.out.println(com2.compare(12, 6));
}

// 语法格式六:当Lambda体只有一条语句时,若有return和大括号,可以考虑省略
@Test
public void test6() {
Comparator<Integer> com = (o1, o2) -> o1.compareTo(o2);
System.out.println(com.compare(12, 21));
}

函数式接口

什么是函数式接口

只包含一个抽象方法的接口,就是函数式接口。可以在接口上使用@FunctionalInterface注解来检查它是否是一个函数式接口。

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

  • 消费型接口Consumer<T>void accept(T t)
  • 供给型接口Supplier<T>T get()
  • 函数型接口Function<T,R>R apply(T t)
  • 断定型接口Predicate<T>boolean test(T t)

使用案例

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
public class CentralFunctionalInterfaces {
@Test
public void test1() {
happyTime(500.0, new Consumer<Double>() {
@Override
public void accept(Double aDouble) {
System.out.println("在天守阁消费" + aDouble);
}
});
System.out.println("========================");
// Lambda写法
happyTime(400.0, money -> System.out.println("在望舒客栈消费" + money));
}

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

@Test
public void test2() {
List<String> list = Arrays.asList("北京", "南京", "东京", "天津", "西京");
List<String> filterString = filterString(list, new Predicate<String>() {
@Override
public boolean test(String s) {
return s.contains("京");
}
});
System.out.println(filterString);
System.out.println("====================");
// Lambda方法
List<String> list1 = filterString(list, s -> s.contains("京"));
System.out.println(list1);
}

// 根据给定的规则,过滤集合中的字符串,此规则由Predicate的方法决定
public List<String> filterString(List<String> list, Predicate<String> pre) {
ArrayList<String> filterList = new ArrayList<>();
for (String s : list) {
if (pre.test(s))
filterList.add(s);
}
return filterList;
}
}

方法引用与构造器引用

方法引用基本介绍

当要传递给Lambda体的操作已经有实现的方法时,可以使用方法引用。方法引用是Lambda表达式的一种语法糖。

方法引用的使用

  • 格式:类(或对象)::方法名
  • 分为三种情况:
    1. 对象::实例方法名
    2. 类::静态方法名
    3. 类::实例方法名

方法引用的使用示例

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
// 情况一:对象::实例方法
@Test
public void test1() {
Consumer<String> con = s -> System.out.println(s);
con.accept("北京");
System.out.println("********************");
// 方法引用
PrintStream ps = System.out;
Consumer<String> con2 = ps::println;
con.accept("上海");
}

// 情况二:类::静态方法
@Test
public void test3() {
Comparator<Integer> com1 = (t1, t2) -> Integer.compare(t1, t2);
System.out.println(com1.compare(12, 21));
System.out.println("-----------分割线------------");
// 方法引用
Comparator<Integer> com2 = Integer::compare;
System.out.println(com2.compare(12, 1));
}

// 情况三:类::实例方法
@Test
public void test5() {
Comparator<String> com1 = (s1, s2) -> s1.compareTo(s2);
System.out.println(com1.compare("abc", "abd"));
// 方法引用
System.out.println("-----------分割线------------");
Comparator<String> com2 = String::compareTo;
System.out.println(com2.compare("c", "a"));
}

构造器引用

构造器引用与方法引用类似,函数式接口的抽象方法的形参列表和构造器的形参列表一致。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 构造器引用
@Test
public void test1() {
Supplier<Employee> supplier1 = () -> new Employee();
System.out.println(supplier1.get());
System.out.println("---------------------");
// 演示使用构造器引用
Supplier<Employee> supplier2 = Employee::new;
System.out.println(supplier2.get());
}

// 数组引用
@Test
public void test4() {
Function<Integer, String[]> function = length -> new String[length];
String[] arr1 = function.apply(5);
System.out.println(Arrays.toString(arr1));
System.out.println("==========================");
Function<Integer, String[]> fun2 = String[]::new;
System.out.println(Arrays.toString(fun2.apply(8)));
}

强大的Stream API

什么是Stream

Stream是Java 8中处理集合的关键抽象概念,用于操作数据源(集合、数组等)所生成的元素序列。

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
// 创建Stream方式一:通过集合
@Test
public void test1() {
List<Employee> employees = EmployeeData.getEmployees();
Stream<Employee> stream = employees.stream();
Stream<Employee> parallelStream = employees.parallelStream();
}

// 创建Stream方式二:通过数组
@Test
public void test2() {
int[] arr = new int[]{10, 2, 5, 6, 78};
IntStream intStream = Arrays.stream(arr);
Employee[] employees = new Employee[]{new Employee(1001, "Tom"), new Employee(1002, "Jerry")};
Stream<Employee> stream = Arrays.stream(employees);
}

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

// 创建Stream方式四:创建无限流
@Test
public void test4() {
Stream.iterate(0, t -> t + 2).limit(10).forEach(System.out::println);
Stream.generate(Math::random).limit(5).forEach(System.out::println);
}

中间操作

  • 筛选与切片

    • filter(Predicate p):从流中排除某些元素。
    • limit(long maxSize):截断流,使其元素不超过给定数量。
    • skip(long n):跳过前n个元素。
    • distinct():去除重复元素。
  • 映射

    • map(Function f):将流中的每个元素映射成一个新的元素。
    • flatMap(Function f):将流中的每个值都换成另一个流,然后把所有流连接成一个流。
  • 排序

    • sorted():按自然顺序排序。
    • sorted(Comparator comp):按比较器顺序排序。

Stream的终止操作

  • 匹配与查找

    • allMatch(Predicate p):检查是否匹配所有元素。
    • anyMatch(Predicate p):检查是否至少匹配一个元素。
    • noneMatch(Predicate p):检查是否没有匹配所有元素。
    • findFirst():返回第一个元素。
    • findAny():返回任意一个元素。
    • count():返回流中元素总数。
    • max(Comparator c):返回流中最大值。
    • min(Comparator c):返回流中最小值。
    • forEach(Consumer c):内部迭代。
  • 归约

    • reduce(T iden, BinaryOperator b):将流中元素反复结合起来,得到一个值。
    • reduce(BinaryOperator b):将流中元素反复结合起来,得到一个值。
  • 收集

    • collect(Collector c):将流转换为其他形式,如集合。

示例代码

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
@Test
public void test1() {
// allMatch(Predicate p) 检查是否匹配所有元素
List<Employee> employees = EmployeeData.getEmployees();
boolean allMatch = employees.stream().allMatch(e -> e.getAge() > 18);
System.out.println(allMatch);

// anyMatch(Predicate p) 检查是否至少匹配一个元素
boolean anyMatch = employees.stream().anyMatch(e -> e.getSalary() > 10000);
System.out.println(anyMatch);

// noneMatch(Predicate p) 检查是否没有匹配所有元素
boolean noneMatch = employees.stream().noneMatch(employee -> employee.getName().startsWith("雷"));
System.out.println(noneMatch);

// findFirst() 返回第一个元素
Optional<Employee> first = employees.stream().findFirst();
System.out.println(first);

// findAny() 返回当前流中的任意元素
Optional<Employee> any = employees.parallelStream().findAny();
System.out.println(any);

// count() 返回流中元素总数
long count = employees.stream().filter(employee -> employee.getSalary() > 3000).count();
System.out.println(count);

// max(Comparator c) 返回流中最大值
Stream<Double> salaryStream = employees.stream().map(Employee::getSalary);
Optional<Double> maxSalary = salaryStream.max(Double::compare);
System.out.println(maxSalary);

// min(Comparator c) 返回流中最小值
Optional<Employee> employee = employees.stream().min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
System.out.println(employee);

// forEach(Consumer c) 内部迭代
employees.stream().forEach(System.out::println);
}

@Test
public void test2() {
// reduce(T iden, BinaryOperator b) 可以将流中元素反复结合起来,得到一个值
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Integer sum = list.stream().reduce(0, Integer::sum);
System.out.println(sum);

// reduce(BinaryOperator b) 可以将流中元素反复结合起来,得到一个值
List<Employee> employees = EmployeeData.getEmployees();
Optional<Double> totalSalary = employees.stream().map(Employee::getSalary).reduce(Double::sum);
System.out.println(totalSalary);
}

@Test
public void test3() {
// collect(Collector c) 将流转换为其他形式
List<Employee> employeeList = EmployeeData.getEmployees().stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toList());
employeeList.forEach(System.out::println);

Set<Employee> employeeSet = EmployeeData.getEmployees().stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toSet());
employeeSet.forEach(System.out::println);
}

Optional类

Optional类介绍

Optional类是一个容器类,代表一个值存在或不存在,可以避免空指针异常。

常用方法

  • Optional.of(T t):创建一个Optional实例,t必须非空。
  • Optional.empty():创建一个空的Optional实例。
  • Optional.ofNullable(T t):t可以为null。
  • isPresent():判断是否包含值。
  • orElse(T t):如果调用对象包含值,返回该值,否则返回t。
  • orElseGet(Supplier s):如果调用对象包含值,返回该值,否则返回s获取的值。
  • map(Function f):如果有值对其处理,并返回处理后的Optional。
  • 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
public class OptionalTest {
@Test
public void test1() {
Girl girl = new Girl();
// of(T t):保证t是非空的
Optional<Girl> optionalGirl = Optional.of(girl);
}

@Test
public void test2() {
Girl girl = new Girl();
// ofNullable(T t):t可以为null
Optional<Girl> optionalGirl = Optional.ofNullable(girl);
System.out.println(optionalGirl);
// orElse(T t1):如果当前的Optional内部封装的t是非空的,则返回内部的t.
// 如果内部的t是空的,则返回orElse()方法中的参数t1.
Girl girl1 = optionalGirl.orElse(new Girl("赵丽颖"));
System.out.println(girl1);
}

// 使用Optional类优化方法
public String getGirlName2(Boy boy) {
Optional<Boy> boyOptional = Optional.ofNullable(boy);
Boy boy1 = boyOptional.orElse(new Boy(new Girl("迪丽热巴")));
Girl girl = boy1.getGirl();
Optional<Girl> girlOptional = Optional.ofNullable(girl);
Girl girl1 = girlOptional.orElse(new Girl("古力娜扎"));
return girl1.getName();
}

@Test
public void test5() {
Boy boy = new Boy(new Girl("ikura"));
String girlName = getGirlName2(boy);
System.out.println(girlName);
}
}

Java8新特性
http://blog.hrseno.cn/2024/03/20/Java-Java8新特性/
作者
黄浩森
发布于
2024年3月20日
许可协议