说说jdk1.8的新特性

从Stream,方法引用,lambda,函数式接口聊

Posted by Zheng Yang on August 4, 2024

JDK1.8的新特性有哪些

Snipaste-2024-08-04-11-58-05.png

一. Lambda表达式

1. 需求分析

创建一个新的线程,指定线程要执行的任务

public class Demo01Lambda {
    public static void main(String[] args) {
        //开启一个新的线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("新线程中执行的代码"+Thread.currentThread().getName());
            }
        }).start();
        System.out.println("主线程中的代码"+Thread.currentThread().getName());
    }
}

2. 代码分析:

  1. Thread类需要一个Runnable接口作为其参数,其中的抽象方法run方法是用来指定线程任务内容的核心。
  2. 为了指定run方法体,不得不需要Runnable的实现类。
  3. 为了省去定义一个Runnable的实现类,不得不使用匿名内部类。
  4. 必须覆盖重写抽象的run方法,所有的方法名称,方法参数,方法返回值不得不都重写一遍,而且不能出错。
  5. 而实际上,我们只在乎方法体中的代码。

3. 使用Lambda表达式

由此我们可以通过使用Lambda表达式,来减轻书写代码负担,简化了匿名内部类的使用。

public class Demo01Lambda {
    public static void main(String[] args) {
        //开启一个新的线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("新线程中执行的代码"+Thread.currentThread().getName());
            }
        }).start();
        System.out.println("主线程中的代码"+Thread.currentThread().getName());
        System.out.println("----------------");
        new Thread(()->{
            System.out.println("新线程Lambda表达式"+Thread.currentThread().getName());
        }).start();
    }
}

4. Lambda表达式语法规则

(参数类型 参数名称) ->{
	代码体
}

5. @FunctionalInterface注解

被该注解修饰的接口只能声明一个抽象方法。

6. Lambda表达式的使用前提

  • 方法中参数或局部变量类型必须为接口才能使用Lambda
  • 接口中有且仅有一个抽象方法(@FunctionalInterface)

二. 方法引用

1. 为什么要用方法的引用

Lambda表达式会出现代码冗余的情况。

2. 方法引用的格式

符号表示::: 符号说明:双冒号为方法引用运算符,而它所在的表达式被称为方法引用。 应用场景:如果Lambda表达式所要实现的方案,已经有其他方法存在相同的方案,那么则可以使用方法引用。 常见引用方式:

  • instanceName::methodName 对象::方法名
  • ClassName::staticMethodName 类名::静态方法
  • ClassName::methodName 类名::普通方法
  • ClassName::new 类名::new调用的构造器
  • TypeName[]::new String[]::new 调用数组的构造器

3. 方法引用案例

public class Test {
    public static void main(String[] args) {
        Date now =new Date();
        Supplier<Long> supplier = ()->{
            return now.getTime();
        };
        System.out.println(supplier.get());
        //如果通过方法的引用来实现
        Supplier<Long> supplier1 = now::getTime;
        System.out.println(supplier1.get());
    }
}

三. Java中stream的API

Java 8引入了Stream API,它提供了一种高效且易于使用的数据处理方式,特别适合集合对象的操作,如过滤、映射、排序等。Stream API不仅可以提高代码的可读性和简洁性,还能利用多核处理器的优势进行并行处理。让我们通过两个具体的例子来感受下Java Stream API带来的便利,对比在Stream API引入之前的传统做法。

1. 过滤并收集满足条件的元素

问题场景:从一个列表中筛选出所有长度大于3的字符串,并收集到一个新的列表中。

没有Stream API的做法

List<String> originalList = Arrays.asList("apple", "fig", "banana", "kiwi");
List<String> filteredList = new ArrayList<>();

for (String item : originalList) {
    if (item.length() > 3) {
        filteredList.add(item);
    }
}

这段代码需要显式地创建一个新的ArrayList,并通过循环遍历原列表,手动检查每个元素是否满足条件,然后添加到新列表中。

使用Stream API的做法

List<String> originalList = Arrays.asList("apple", "fig", "banana", "kiwi");
List<String> filteredList = originalList.stream()
                                        .filter(s -> s.length() > 3)
                                        .collect(Collectors.toList());

这里,我们直接在原始列表上调用.stream()方法创建了一个流,使用.filter()中间操作筛选出长度大于3的字符串,最后使用.collect(Collectors.toList())终端操作将结果收集到一个新的列表中。代码更加简洁明了,逻辑一目了然

2. 计算列表中所有数字的总和

问题场景:计算一个数字列表中所有元素的总和。

没有Stream API的做法

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = 0;
for (Integer number : numbers) {
    sum += number;
}

这个传统的for-each循环遍历列表中的每一个元素,累加它们的值来计算总和。

使用Stream API的做法

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream()
                 .mapToInt(Integer::intValue)
                 .sum();

通过Stream API,我们可以先使用.mapToInt()将Integer流转换为IntStream(这是为了高效处理基本类型),然后直接调用.sum()方法来计算总和,极大地简化了代码。

四. 函数式接口

我们知道使用Lambda表达式的前提是需要有函数式接口,而Lambda表达式使用时不关心接口名,抽象方法名。只关心抽象方法的参数列表和返回值类型。因此为了让我们使用Lambda表达式更加的方便,在JDK中提供了大量常用的函数式接口。

1. 函数式接口的介绍

Supplier 无参有返回值的接口

Function 有参有返回值的接口

2. 举例

以supplier为例

public class Test {
    public static void main(String[] args) {
        fun(()->{
           int arr[]={1,2,3,55,5};
           //计算数组中最大值
            Arrays.sort(arr);
            return arr[arr.length-1];
        });
    }
    public static void fun(Supplier<Integer> supplier) {
        Integer max = supplier.get();
        System.out.println("max = "+max);
    }
}