Java8新特性

本文详细介绍了Java8的新特性,包括Lambda表达式的概念和语法、函数式接口的使用、引用的三种类型及其应用场景、Stream API的各个操作以及Optional类的作用和常用方法。通过这些特性,Java8提升了代码的简洁性和灵活性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、Lambda表达式

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

匿名内部类:

@Test
public void test01(){
    //匿名内部类
    Comparator<Integer> comparator = new Comparator<Integer>() {
        @Override
        public int compare(Integer o1, Integer o2) {
            return Integer.compare(o1,o2);
        }

        @Override
        public boolean equals(Object obj) {
            return false;
        }
    };
    //调用
    TreeSet<Integer> set = new TreeSet<>(comparator);
}

Lambda写法:

@Test
public void test02(){
    // Lambda 表达式
    Comparator<Integer> comparator = (a, b) -> Integer.compare(a, b);

    TreeSet<Integer> set = new TreeSet<>(comparator);
}

Lambda语法:

  • 操作符:->
  • 左侧:参数列表
  • 右侧:执行代码块 / Lambda 体

口诀:

  • 写死小括号,拷贝右箭头,落地大括号
  • 左右遇一括号省
  • 左侧推断类型省

语法格式:
a、无参数,无返回值

例如 Runnable接口:
public class Test02 {
	int num = 10; //jdk 1.7以前 必须final修饰
    
    @Test
    public void test01(){
        // 匿名内部类
        new Runnable() {
            @Override
            public void run() {
                // 在局部类中引用同级局部变量
                // 只读
                System.out.println("Hello Lambda" + num);
            }
        };
    }

    @Test
    public void test02(){
        // 语法糖
     	Runnable runnable = () -> {
         	System.out.println("Hello Lambda");
     	};
    }
}

b、有一个参数,无返回值

@Test
public void test03(){
    // 一个参数,没有返回值
    Consumer<String> consumer = (a) -> System.out.println(a);
    consumer.accept("Hello Lambda!");

    // 一个参数,没有返回值(小括号可以省略不写)
    Consumer<String> consumer1 = a -> System.out.println(a);
    consumer1.accept("Hello Lambda!");
}

c、有两个及以上的参数,有返回值

// 有两个及以上的参数,有返回值(并且 Lambda 体中有多条语句)
Comparator<Integer> comparator = (a, b) -> {
    System.out.println("比较接口");
    return Integer.compare(a, b);
};

// 测试
ArrayList<Integer> list = new ArrayList<>();
list.add(3);
list.add(2);
list.add(1);
System.out.println(list); // [3, 2, 1]
list.sort(comparator);
System.out.println(list); // [1, 2, 3]

// 有两个及以上的参数,有返回值,并且 Lambda 体中只有1条语句 (大括号 与 return 都可以省略不写)
Comparator<Integer> comparator = (a, b) -> Integer.compare(a, b);

2、函数式接口

java给我们提供了很多预置好的接口(各种参数和返回值),不用我们自己定义,方便使用。

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

a、Consumer 消费型接口(有去无回)

    @Test
    public void test01(){
        //Consumer
        Consumer<Integer> consumer = (x) -> System.out.println("消费型接口:" + x);
        //test
        consumer.accept(100);
    }

b、Supplier 提供型接口

没有参数,有返回
    @Test
    public void test02(){
        // Supplier
        Supplier<ArrayList<String>> supplier = () -> {
            ArrayList<String> list = new ArrayList<>();
            list.add("test");
            list.add("qiang");
            return list;
        };

        ArrayList<String> strings = supplier.get();
        System.out.println(strings); // [test, qiang]
    }

c、Function<T, R> 函数型接口

有去有回
    @Test
    public void test03(){
        //Function<T, R>
        String oldStr = "abc123456xyz";
        Function<String, String> function = (s) -> s.substring(1, s.length()-1);
        //test
        System.out.println(function.apply(oldStr)); // bc123456xy
    }

d、Predicate 断言型接口

返回布尔值
    @Test
    public void test04(){
        //Predicate<T>
        Integer age = 61;
        Predicate<Integer> predicate = (i) -> i >= 60;
        if (predicate.test(age)){
            System.out.println("年龄大于等于60"); // 年龄大于等于60
        } else {
            System.out.println("年龄小于60");
        }
    } 

e、其它派生接口

在这里插入图片描述

3、引用

a、方法引用

若 Lambda 表达式体中的内容已有方法实现,则我们可以使用“方法引用”

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

注意:Lambda 表达实体中调用方法的参数列表、返回类型必须和函数式接口中抽象方法保持一致

对象::实例方法

    @Test
    public void test01(){
        PrintStream ps = System.out;
    
        // Lambda表达式的写法
        Consumer<String> con1 = (s) -> ps.println(s);
        con1.accept("aaa");
    
        // 引用的写法(Consumer是有参无返回值,println(s)也是有参无返回值,所以可以使用引用)
        Consumer<String> con2 = ps::println;
        con2.accept("bbb");
    }

类::静态方法

    @Test
    public void test02(){
        Comparator<Integer> com1 = (x, y) -> Integer.compare(x, y);
        System.out.println(com1.compare(1, 3)); // -1

        Comparator<Integer> com2 = Integer::compare;
        System.out.println(com2.compare(3, 1)); // 1
    }

类::实例方法
使用条件:Lambda参数列表中的第一个参数是方法的调用者,第二个参数是方法的参数时,才能使用 ClassName :: Method

    @Test
    public void test03(){
        BiPredicate<String, String> bp1 = (x, y) -> x.equals(y);
        System.out.println(bp1.test("a","b")); // false

        // 使用条件:第一个参数x是调用者,第二个参数y是方法的参数,所以可以用【类::实例方法】
        BiPredicate<String, String> bp2 = String::equals;
        System.out.println(bp2.test("c","c")); // true
    }

在这里插入图片描述

另一种场景:在map中也可以直接使用
使用方法引用(使用条件:a为参数,a也是调用者,第二参数为空。同样满足)

// 使用方法引用(使用条件:a为参数,a也是调用者,第二参数为空。同样满足)
List<String> collect2 = list.stream().map(User::getName).collect(Collectors.toList());
System.out.println(collect2);

b、构造器引用

格式:ClassName :: new
使用条件:**需要调用的构造器的参数列表要与函数时接口中抽象方法的参数列表保持一致**
    @Test
    public void test04(){
        // Lambda表达式写法
        Supplier<List> sup1 = () -> new ArrayList();

        // 引用写法(无参有返回值,和Supplier参数列表一致,所以可以使用构造器引用)
        Supplier<List> sup2 = ArrayList::new;
    }

c、数组引用

语法:Type[]::new;
    @Test
    public void test05(){
        Function<Integer,String[]> fun1 = (x) -> new String[x]; // lambda表达式写法(传递int值,返回数组)
        Function<Integer,String[]> fun2 = String[]::new; // 引用写法

        // 测试
        String[] apply = fun1.apply(2);
        System.out.println(apply.length); // 2

        // 测试
        String[] apply1 = fun2.apply(3);
        System.out.println(apply1.length); // 3
    }

4、Stream API

Stream 自己不会存储元素。
Stream不会改变源对象。相反,他们会返回一个持有结果的新Stream。
Stream操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。

Stream操作步骤:

  • 创建Stream
  • 中间操作
  • 终止操作
    在这里插入图片描述

a、创建流

/**
* 创建流
*/
@Test
public void test01(){
    /**
    * 集合流
    *  - Collection.stream() 穿行流
    *  - Collection.parallelStream() 并行流
    */
    List<String> list = new ArrayList<>();
    Stream<String> stream1 = list.stream();

    //数组流
    //Arrays.stream(array)
    String[] strings = new String[10];
    Stream<String> stream2 = Arrays.stream(strings);

    //Stream 静态方法
    //Stream.of(...)
    Stream<Integer> stream3 = Stream.of(1, 2, 3);
}

b、筛选 / 切片

中间操作:

  • filter:接收 Lambda ,从流中排除某些元素
  • limit:截断流,使其元素不超过给定数量
  • skip(n):跳过元素,返回一个舍弃了前n个元素的流;若流中元素不足n个,则返回一个空流;与limit(n) 互补
  • distinct:筛选,通过流所生成的 hashCode() 与 equals() 取除重复元素
    在这里插入图片描述
    List<Employee> emps = Arrays.asList(
            new Employee(101, "Z3", 19, 9999.99),
            new Employee(102, "L4", 20, 7777.77),
            new Employee(103, "W5", 35, 6666.66),
            new Employee(104, "Tom", 44, 1111.11),
            new Employee(105, "Jerry", 60, 4444.44)
    );

    @Test
    public void test02(){
        emps.stream()
                .filter((x) -> x.getAge() > 35) // 返回Stream
                .limit(3) //短路?达到满足不再内部迭代
                .distinct()
                .skip(1) // 把44岁的去掉了
                .forEach(System.out::println);
        // Employee{id=105, name='Jerry', age=60, salary=4444.44}
    }

备注:limit满足条件后就不会再迭代了,提高了性能。

c、映射

  • map:接收 Lambda ,将元素转换为其他形式或提取信息;接受一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
  • flatMap:接收一个函数作为参数,将流中每一个值都换成另一个流,然后把所有流重新连接成一个流
    @Test
    public void test03(){
        List<String> list = Arrays.asList("a", "b", "c");
        list.stream()
                .map((str) -> str.toUpperCase())
                .forEach(System.out::println); // A B C
    }

d、排序

  • sorted():自然排序
  • sorted(Comparator c):定制排序
    @Test
    public void test05(){
        List<Integer> list = Arrays.asList(1,2,4,3,5);
        list.stream()
                .sorted() //comparaTo()
                .forEach(System.out::println);  // 1 2 3 4 5
    }

Comparator:定制排序

    @Test
    public void test06(){
        emps.stream()
            .sorted((e1, e2) -> { //compara()
                if (e1.getAge().equals(e2.getAge())){
                    return e1.getName().compareTo(e2.getName());
                } else {
                    return e1.getAge().compareTo(e2.getAge());
                }
            })
            .forEach(System.out::println);
    }

输出结果:
在这里插入图片描述

e、查找 / 匹配

终止操作:

  • allMatch:检查是否匹配所有元素
  • anyMatch:检查是否至少匹配一个元素
  • noneMatch:检查是否没有匹配所有元素
  • findFirst:返回第一个元素
  • findAny:返回当前流中的任意元素
  • count:返回流中元素的总个数
  • max:返回流中最大值
  • min:返回流中最小值
    @Test
    public void test07(){
        List<Status> list = Arrays.asList(Status.FREE, Status.BUSY, Status.VOCATION);

        boolean flag1 = list.stream()
                .allMatch((s) -> s.equals(Status.BUSY));
        System.out.println(flag1); // false

        boolean flag2 = list.stream()
                .anyMatch((s) -> s.equals(Status.BUSY));
        System.out.println(flag2); // true

        boolean flag3 = list.stream()
                .noneMatch((s) -> s.equals(Status.BUSY));
        System.out.println(flag3); // false

        // 避免空指针异常
        Optional<Status> op1 = list.stream()
                .findFirst();
        // 如果Optional为空 找一个替代的对象
        Status s1 = op1.orElse(Status.BUSY);
        System.out.println(s1); // FREE

        Optional<Status> op2 = list.stream()
                .findAny();
        System.out.println(op2); // Optional[FREE]

        long count = list.stream()
                .count();
        System.out.println(count); // 3
    }

f、归约 / 收集(常用)

  • 归约:reduce(T identity, BinaryOperator) / reduce(BinaryOperator) 可以将流中的数据反复结合起来,得到一个值
  • 收集:collect 将流转换成其他形式;接收一个 Collector 接口的实现,用于给流中元素做汇总的方法
    在这里插入图片描述

reduce测试:

    /**
     * Java:
     *  - reduce:需提供默认值(初始值)
     * Kotlin:
     *  - fold:不需要默认值(初始值)
     *  - reduce:需提供默认值(初始值)
     */
    @Test
    public void test08(){
        List<Integer> list = Arrays.asList(1, 2, 3, 4);
        Integer integer = list.stream()
                .reduce(0, (x, y) -> x + y);
        System.out.println(integer); // 10
    }

收集测试:

@Test
public void test09(){
    //放入List
    List<String> list = emps.stream()
            .map(Employee::getName)
            .collect(Collectors.toList());
    list.forEach(System.out::println);

    //放入Set
    Set<String> set = emps.stream()
            .map(Employee::getName)
            .collect(Collectors.toSet());
    set.forEach(System.out::println);

    //放入LinkedHashSet
    LinkedHashSet<String> linkedHashSet = emps.stream()
            .map(Employee::getName)
            .collect(Collectors.toCollection(LinkedHashSet::new));
    linkedHashSet.forEach(System.out::println);
}

收集(计算):

    @Test
    public void test10(){
        //总数
        Long count = emps.stream()
                .collect(Collectors.counting());
        System.out.println(count);  // 5

        //平均值
        Double avg = emps.stream()
                .collect(Collectors.averagingDouble(Employee::getSalary));
        System.out.println(avg); // 5999.994000000001

        //总和
        Double sum = emps.stream()
                .collect(Collectors.summingDouble(Employee::getSalary));
        System.out.println(sum); // 29999.97 

        //最大值
        Optional<Employee> max = emps.stream()
                .collect(Collectors.maxBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
        System.out.println(max.get()); // Employee{id=101, name='Z3', age=19, salary=9999.99}

        //最小值
        Optional<Double> min = emps.stream()
                .map(Employee::getSalary)
                .collect(Collectors.minBy(Double::compare));
        System.out.println(min.get()); // 1111.11
    }

收集(分组、分区)

    @Test
    public void test11(){
        //分组
        Map<Integer, List<Employee>> map = emps.stream()
                .collect(Collectors.groupingBy(Employee::getId));
        System.out.println(map);

        //多级分组
        Map<Integer, Map<String, List<Employee>>> mapMap = emps.stream()
                .collect(Collectors.groupingBy(Employee::getId, Collectors.groupingBy((e) -> {
                    if (e.getAge() > 35) {
                        return "开除";
                    } else {
                        return "继续加班";
                    }
                })));
        System.out.println(mapMap);

        //分区
        Map<Boolean, List<Employee>> listMap = emps.stream()
                .collect(Collectors.partitioningBy((e) -> e.getSalary() > 4321));
        System.out.println(listMap);
    }

5、Optional

定义:Optional 类 (java.util.Optional) 是一个容器类,代表一个值存在或不存在,原来用 null 表示一个值不存在,现在用 Optional 可以更好的表达这个概念;并且可以避免空指针异常
常用方法:

  • Optional.of(T t):创建一个 Optional 实例
  • Optional.empty(T t):创建一个空的 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.of(T t):

    @Test
    public void test01(){
        Optional<Employee> op = Optional.of(new Employee());
        Employee employee = op.get();
        System.out.println(employee); // Employee{id=null, name='null', age=null, salary=null}
    }

Optional.empty(T t):

    @Test
    public void test02(){
        Optional<Employee> op = Optional.empty();
        Employee employee = op.get(); // 报错 java.util.NoSuchElementException: No value present
    }

Optional.ofNullable(T t):

    @Test
    public void test03(){
        Optional<Employee> op = Optional.ofNullable(new Employee());
        Employee employee = op.get();
        System.out.println(employee); // Employee{id=null, name='null', age=null, salary=null}
    }

isPresent():

    @Test
    public void test04(){
        Optional<Employee> op = Optional.ofNullable(new Employee());
        if (op.isPresent()) {
            Employee employee = op.get();
            System.out.println(employee); // Employee{id=null, name='null', age=null, salary=null}
        }
    }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值