• Java8新特性


    一、Stream流

    1. Stream简介

    Stream允许我们以声明式的方式处理数据集合,可以将Stream看作是遍历数据集合的高级迭代器。Stream 与Lambda表达式结合使用,将使编码效率大大提高,可读性增强。

    注意:Stream与IO中的InputStream/OutputStream是不同的概念

    操作案例:在这里插入图片描述使用stream将集合中的手机按照序号num从小到大排序:
    在这里插入图片描述
    结果:
    在这里插入图片描述
    Stream流的操作顺序

    在这里插入图片描述
    Stream语法

    | stream of elements +-----> |filter+-> |sorted+-> |map+-> |collect|
    
    • 1

    2. Stream用法

    2. 1获取流

    1. Collection接口下的stream()获取流

    在这里插入图片描述

    List<String> list = new ArrayList<>();
    Stream<String> stream = list.stream();
    
    • 1
    • 2
    1. 使用 Arrays 中的 stream() 方法,将数组转成流

    在这里插入图片描述

    Integer[] arr= new Integer[10]; 
    Stream<Integer> stream = Arrays.stream(arr);
    
    • 1
    • 2
    1. 使用 Stream 中的静态方法:of()

    在这里插入图片描述

    Stream<Integer> stream = Stream.of(4,3,2,7,5,1,6);
    
    • 1
    1. 使用 BufferedReader.lines() 方法,将每行内容转成流

    在这里插入图片描述

    BufferedReader reader=new BufferedReader(new FileReader("test.txt")); 
    Stream<String> lineStream = reader.lines();
    
    • 1
    • 2

    2.2 流操作

    流操作可以分为中间操作终端操作

    List<Apple> apples = 
    			applestore .stream() 获得流
    			 		   .filter(a -> a.getColor().equals("红色")) 中间操作
    			 		   .collect(Collectors.toList()); 终端操作
    
    • 1
    • 2
    • 3
    • 4

    简单来说Stream的流程就是
    数据源->中间操作->终端操作->结果

    中间操作
    • filter():过滤流中的某些元素
    • sorted():自然排序,流中元素需实现Comparable接口
    • distinct():去除重复元素
    • limit(n):获取n个元素
    • skip(n):跳过n个元素,配合limit(n)可实现分页
    • map():将其映射为一个新的元素
      在这里插入图片描述
      在这里插入图片描述
      map():
    1. 对集合中的每一个元素求平方
      在这里插入图片描述
      在这里插入图片描述
      2.使用map()将String型的整数转为Integer型的整数
      在这里插入图片描述
      在这里插入图片描述
    终端操作
    • forEach():遍历流中的元素
    • toArray():将流中的元素倒入一个数组中
    • Min():返回流中元素的最小值
    • Max():返回流中元素的最大值
    • count():返回流中元素的总个数
    • reduce():所有元素求和
    • anyMatch():接收一个Predicate函数,只要流中有一个元素满足条件则返回true,否则返回false
    • allMatch():接收一个Predicate函数,当流中所有元素都符合条件时才返回true,否则返回false
    • findFirst():返回流中的第一个元素
    • collect():将流中的元素倒入一个集合(Collection/Map)
    /**
             * 终端操作 -- 结果
             */
            Integer[] arr = new Integer[]{3,1,5,7,2,6,4,4,0};
            Object[] arr1 =  Arrays.stream(arr)
                    .distinct()
                    .sorted()
                    .toArray();        //toArray()将流中元素倒入Object数组中
            //forEach()
            Arrays.stream(arr1).forEach(e-> System.out.print(e+" "));
            //count 统计
            Long count = Arrays.stream(arr).count();
            System.out.println(count);
            //anyMatch 流中任意一个元素符合条件时返回true
            boolean any = Arrays.stream(arr).anyMatch((e)->{return e>3;});
            System.out.println("有1个元素>3 :"+any);
            //anyMatch 流中所有元素符合条件时返回true
            boolean all = Arrays.stream(arr).allMatch((e)->{return e>3;});
            System.out.println("所有元素>3 :"+all);
            //findFirst 返回流中第一个元素
            System.out.println("流中第一个元素 :"+Arrays.stream(arr).findFirst());
            //reduce:所有元素求和
            Optional<Integer> res = Arrays.stream(arr).reduce((a, b)->a+b);
            System.out.println("元素求和 :"+res);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    在这里插入图片描述

    Map 的 Stream用法 示例

    根据对象ID属性将java集合拆分为子集合

    Map<Integer, List<LotRealTimeProductionInfo>> retByIdMap =
                    lotRetInfoList.stream().collect(Collectors.groupingBy(LotRealTimeProductionInfo::getLotId));
    
    • 1
    • 2

    二、Lambda表达式

    Lambda表达式是Java8增加的语言级的新特性。

    1.背景

    在Java中,一切皆对象, 数组、类的实例都是对象。

    在Java中定义函数或者方法不可能完全独立,也不能将方法作为参数或返回一个方法给实例。

    在Java8之前,要实现将某些功能传递给某个方法,要写匿名内部类:

    		String[] arr = {"c","a","d","b"};
            //Arrays.sort(arr,new StringSort());// 外部类
            //Arrays.sort(arr,new InnerStringSort());// 内部类
            //new + 接口/抽象类 创建匿名内部类对象
            Arrays.sort(arr, new Comparator<String>() {
            	@Override
                public int compare(String o1, String o2) {
                    return o2.compareTo(o1);
                }
            });
            System.out.println(Arrays.toString(arr));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在这里插入图片描述

    为了对集合进行排序,为Comparator接口创建了一个匿名内部类对象,重写了接口中的方法来实现排序的功能。

    2.简介

    Lambda表达式是一个匿名函数,可以将Lambda表达式理解为一段可以传递的代码(将代码段数据一样传递)。使用Lambda表达式可以写出更简洁,更灵活的代码。

    Lambda表达式的本质只是一个语法糖,有编译器推断并帮助我们转换包装为常规的代码,因此我们可以使用更少的代码来实现相同的功能。

    Java中的Lambda表达式通常使用(argument)->{body}的语法书写:

    	->的左侧:Lambda表达式的参数列表
    	->的右侧:Lambda表达式中需要执行的功能,及Lambda(参数1,参数2,...)->{函数体}
    	(类 类实例,类 类实例,...)->{函数体}
    
    • 1
    • 2
    • 3
    • 4

    下面是一些Lambda表达式的例子:

    • 无参数,无返回值,lambda 体中只有一行代码时,{}可以忽略
      () -> System.out.println("Hello World");
    • 无参数,有返回值
      () -> { return 3.1415 };
    • 有参数,无返回值
      (String s) -> { System.out.println(s); }
    • 有一个参数,无返回值
      s -> { System.out.println(s); }
    • 有多个参数,有返回值
      (int a, int b) -> { return a + b; }
    • 有多个参数,表达式参数类型可以不写,jvm 可以根据上下文进行类型推断
      (a, b) -> { return a - b; }

    3.Lambda表达式的结构

    • Lambda表达式参数可以有0个、1个或多个正文语句可以有0条,1条或多条
    • 可以显式声明参数类型,也可以由编译器自动从上下位推断参数的类型。
      (int a,int b)与 (a,b)相同
    • 空括号表示无参 ()->100
    • 正文只有一条语句,大括号不用写,且表达式返回值类型与匿名函数的返回类型相同。(a,b)->return a+b;
    • 只有一个参数时,如果不指明类型,那么可以不写括号x->return x*x;
    package com.ffyc.forword.lambd;
    
    import java.util.Arrays;
    
    public class LambdDemo {
        /*
            lambda 语法
                (参数列表)->{函数体}
            是匿名函数,一般在方法的参数为"接口"时,代替匿名内部类使用(Java8新特性)
            (a,b) 参数类型可以不写,可以根据上下文自动推断
                推断依据?
                    要求接口中只有一个抽象方法
                    @FunctionalInterface  功能接口(java8)单抽象方法接口
                    public interface Comparator
        */
    
        public static void main(String[] args) {
            String[] arr = {"c","a","d","b"};
            //用lambda表达式代替匿名内部类对象  将接口中的函数作为参数传递
            Arrays.sort(arr,(o1,o2)->{ return o2.compareTo(o1);});
            System.out.println(Arrays.toString(arr));
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    在这里插入图片描述

    4.Lambda表达式 示例

    1. 线程初始化
    //匿名内部类方式
    new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("run...");
        }
    }).start();
    
    //Lambda表达式    
    new Thread( ()->{System.out.println("run...");} ).start();             
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    1. 排序
    Arrays.sort(arr, new Comparator<String>() {
    	@Override
        public int compare(String o1, String o2) {
            return o1.compareTo(o2);
        }
    });
    
    Arrays.sort(arr,(o1,o2)->{ return o1.compareTo(o2);});
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    	List<Integer> list = Arrays.asList(4,1,3,2);
        System.out.println(list);//[4, 1, 3, 2]
        list.sort((o1,o2)->{return o1.compareTo(o2);});
        System.out.println(list);//[1, 2, 3, 4]
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    1. 事件处理
    // 匿名内部类方式
    button.addActionListener(new ActionListener() { 
    	@Override 
    	public void actionPerformed(ActionEvent e) {
    	   System.out.println("Hello world"); 
    	} 
    }); 
    
    // lambda 表达式方式 
    button.addActionListener( (e) -> { System.out.println("Hello world"); });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    1. 遍历输出
    	List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
            //foreach遍历
            for (Integer i : list) {
                System.out.print(i+" ");
            }
            //Lambda表达式
            list.forEach(i->{
                System.out.print(i+" ");
            });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    5.lambda表达式实现原理

    lambda是用方法实现的

    三、函数式接口(Functional Interface)

    功能接口是Java8中新增特性,只允许一个抽象方法。这些接口也称为单抽象方法接口。它们也可以使用Lambda表达式,方法引用和构造函数引用来表示。

    Java8引入了一个注释:@FunctionalInterface,当我们注释的接口违反了Functional Interface的规则时,会编译报错。

    自定义功能接口示例:
    在这里插入图片描述
    功能接口只能有一个抽象方法,如果在被@FunctionalInterface标注的接口中声明超过1个抽象方法,会抛出编译错误
    在这里插入图片描述
    Multiple non-overriding abstract methods found in interface …
    在该接口中发现了多个未重写的抽象方法

    Java8允许我们以Lambda表达式的方式为功能接口提供实现,也就是说我们可以将整个Lambda表达式作为功能接口的实现类
    例如:
    功能接口
    在这里插入图片描述
    lambda表达式实现接口
    在这里插入图片描述
    在这里插入图片描述
    Java8 内置核心四大函数式接口
    在这里插入图片描述
    其他函数式接口
    在这里插入图片描述

    四、Optional类–解决空指针问题

    Optional类可以解决空指针问题

    以前处理空指针

    	/**
         * 以前处理空指针
         */
        @Test
        public void test01(){
            //String name = "张三";
            String name = null;
            if(name!=null){
                System.out.println(name.length());
            }else{
                System.out.println("is null");
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    Optional类(自1.8之后)是一个没有子类的工具类,是一个值可以为Null的容器,主要作用是防止NullPointerException

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-75VzLilM-1645615215062)(C:/Users/lanlei/AppData/Roaming/Typora/typora-user-images/1645608291225.png)]

    Optional的常用方法:

    创建Optional实例

    • Optional.of(T t) : 创建一个 Optional的实例对象, of()不支持null
    • Optional.empty(): 创建一个空的Optional实例
    • Optional.ofNullable(T t) : t为null创建空的Optional实例,不为null创建Optional实例
    /**
         * 创建Optional对象
         */
        @Test
        public void test02(){
            //of(T t) 不支持null
            Optional<String> op1 = Optional.of("hello");//Optional[hello]
            //Optional op2 = Optional.of(null);//NullPointerException
    
            //empty) 创建一个空的实例
            Optional<String> op3 = Optional.empty();//Optional.empty
    
            //ofNullable(T t) 支持null
            Optional<String> op4 = Optional.ofNullable("hello");//Optional[hello]
            Optional<String> op5 = Optional.ofNullable(null);//Optional.empty
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    其他常用方法

    • get() : 如果Optional有值就返回,否则抛出NoSuchElementException异常

    • isPresent() : 判断对象是否包含值,包含返回true,否则返回false

    • orElse(T t) : 如果对象包含值,返回该值,否则返回t

    • orElseGet(Supplier other) : 如果对象包含值,返回改值,否则返回Lambda表达式的返回值

    • ifPresent() : 如果值存在则使用该值调用 consumer , 否则不做任何事情。

    /**
         * 常用方法
         * get():如果Optional有值就返回,否则抛出`NoSuchElementException`异常
         * isPresent(): 判断对象是否包含值,包含返回true,否则返回false
         * orElse(T t) : 如果对象包含值,返回该值,否则返回t
         * orElseGet(Supplier other) : 如果对象包含值,返回改值,否则返回Lambda表达式的返回值
         */
        @Test
        public void test03(){
            Optional<String> op1 = Optional.of("hello");
            Optional<String> op2 = Optional.empty();
    
            if(op1.isPresent()){
                System.out.println(op1.get());//hello
            }
            //System.out.println(op2.get());//抛出 NoSuchElementException 异常
    
            String s1 = op1.orElse("张三");
            System.out.println(s1);//hello  -->对象包含值,返回包含的值
            String s2 = op2.orElse("李四");
            System.out.println(s2);//李四   --> 对象不包含值,返回参数值
    
            String s3 = op2.orElseGet(() -> {
                return "hello";
            });
            System.out.println(s3);//hello -->对象不包含值,返回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
    	@Test
        public void test04(){
            Optional<String> op1 = Optional.of("hello");
            Optional<String> op2 = Optional.empty();
    
            //如果存在值,就执行...操作
            //op1.ifPresent(s-> System.out.println(s));
            op1.ifPresent(System.out::println);//hello
            op2.ifPresent(System.out::println);//没有值,不执行打印操作
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    五、方法引用

    对象::实例方法名

    //Supplier中的T get()
    //Employee中的String getName()
    @Test
    public void test2() {
        Employee emp = new Employee(1001,"Tom",23,5600);
        Supplier<String> sup1 = () -> emp.getName();
        System.out.println(sup1.get());
    
        Supplier<String> sup2 = emp::getName;
        System.out.println(sup2.get());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    类::静态方法名

    //Function中的R apply(T t)
    //Math中的Long round(Double d)
    @Test
    public void test4() {
        Function<Double,Long> func1 = d -> Math.round(d);
        System.out.println(func1.apply(12.3));
    
        Function<Double,Long> func2 = Math::round;
        System.out.println(func2.apply(12.6));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    类::实例方法名

    // Comparator中的int comapre(T t1,T t2)
    // String中的int t1.compareTo(t2)
    @Test
    public void test5() {
        Comparator<String> com1 = (s1,s2) -> s1.compareTo(s2);
        System.out.println(com1.compare("abc","abd"));
    
        Comparator<String> com2 = String::compareTo;
        System.out.println(com2.compare("abc","abe"));
    
    }
    
    // Function中的R apply(T t)
    // Employee中的String getName();
    @Test
    public void test7() {
        Employee employee1 = new Employee(1001, "Jerry", 23, 6000);
        Function<Employee,String> func1 = e -> e.getName();
        System.out.println(func1.apply(employee1));
    
        Employee employee2 = new Employee(1001, "tom", 23, 6000);
        Function<Employee,String> func2 = Employee ::getName;
        System.out.println(func2.apply(employee2));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    六、构造器引用、数组引用

    • 构造器引用格式: ClassName::new
    • 数组引用格式:type[] :: new

    构造器引用要求构造器参数列表要与接口中抽象方法的参数列表一致!
    且方法的返回值即为构造器对应类的对象

    在这里插入图片描述
    数组引用

    e

    七、接口中静态方法和默认方法

    Java8之前接口中只能有全局常量和抽象方法
    Java8引入了静态方法和默认方法

    静态方法

    接口名称.静态方法名(参数)

    默认方法

    Java8之前,在接口中只能声明抽象方法,不能声明有方法体的方法。

    default方法是为了在原接口中扩展功能,但是对接口的实现类不会造成影响而出现的

    例如Java源码中Iterable接口forEach和spliterator方法

    default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    //spliterator既可以像Iterator那样逐个迭代,也可以批量迭代。
    //批量迭代可以降低迭代的开销。
    default Spliterator<T> spliterator() {
         return Spliterators.spliteratorUnknownSize(iterator(), 0);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    默认方法的继承问题

    默认方法可以被继承,如果继承多个接口,多个接口有相同的默认方法,实现类需要重写默认方法

    1. 类优先:父类与接口定义了同名方法,默认实现父类的
    2. 优先重现子接口的更具体的方法
    3. 接口冲突:多个接口有参数列表和方法名相同的方法,不管是不是默认方法,实现类都必须去重写,否则程序不知道该调用哪个
      在这里插入图片描述
  • 相关阅读:
    如何学习VBA_3.2.18:DIR函数的补充说明
    【Egg从基础到进阶】一:Egg项目初始化及基础入门
    UML(类图进阶和对象图)
    python经典百题之乒乓球比赛
    Visual Assist 代码辅助检查和重构
    【IEEE会议】第四届IEEE信息科学与教育国际学术会议(ICISE-IE 2023)
    npm源管理工具nrm
    09链表-单链表移除元素
    【前端知识】Three 学习日志(十一)—— 高光网格材质Phong
    解决安装sentry执行install.sh卡住的问题
  • 原文地址:https://blog.csdn.net/lanleihhh/article/details/127726251