Java8的其它新特性

Java8的其它新特性

Lambda表达式

1. Lambda表达式使用前后的对比

  • 举例一

    @Test
    public void test1(){
        Runnable r1 = new Runnable(){
    
            @Override
            public void run() {
                System.out.println("测试1");
            }
        };
    
        r1.run();
    
        System.out.println("*****************");
    
        Runnable r2 = () -> System.out.println("测试2");
    
        r2.run();
    }
    
  • 举例二

    @Test
    public void test2(){
    
        Comparator<Integer> com1 = new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return Integer.compare(o1,o2);
            }
        };
    
        int compare1 = com1.compare(1, 2);
        System.out.println(compare1);
    
        System.out.println("**********************");
        //Lambda表达式的写法
        Comparator<Integer> com2 = (o1, o2) -> Integer.compare(o1, o2);
    
        int compare2 = com2.compare(3, 2);
        System.out.println(compare2);
    
        System.out.println("**********************");
        //方法引用
        Comparator<Integer> com3 = Integer :: compare;
    
        int compare3 = com3.compare(3, 4);
        System.out.println(compare3);
    }
    

2. Lambda表达式的基本语法

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

  • 格式:

    ->:Lambda操作符 或 箭头操作符

    ->左边:Lambda形参列表(接口中抽象方法的形参列表)

    ->右边:Lambda体(重写的抽象方法的方法体)

3. 如何使用

六种情况:

//语法格式一:无参,无返回值
@Test
public void test1(){
    Runnable r1 = new Runnable(){

        @Override
        public void run() {
            System.out.println("测试1");
        }
    };

    r1.run();

    System.out.println("*****************");

    Runnable r2 = () -> {
        System.out.println("测试2");
    };

    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("测试3");

    System.out.println("***************");

    Consumer<String> con1 = (String s) -> {
        System.out.println(s);
    };

    con1.accept("测试4");
}

//语法格式三:数据类型可以省略,因为可由编译器推断得出,称为“类型推断”
@Test
public void test3(){
    Consumer<String> con1 = (String s) -> {
        System.out.println(s);
    };

    con1.accept("测试4");

    System.out.println("****************");

    Consumer<String> con2 = (s) -> {
        System.out.println(s);
    };

    con2.accept("测试4");

    ArrayList<String> list = new ArrayList<>();//类型推断

    int[] arr = {1,2,3};//类型推断
}

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

    con1.accept("测试4");

    System.out.println("*****************");

    Consumer<String> con2 = s -> {
        System.out.println(s);
    };

    con2.accept("测试4");
}

//语法格式五:Lambda需要两个或以上的参数,多条执行语句,并可以有返回值
@Test
public void test5(){
    Comparator<Integer> com1 = 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(com1.compare(1, 2));

    System.out.println("***********************");

    Comparator<Integer> com2 = (o1,o2) -> {
        System.out.println(o1);
        System.out.println(o2);
        return o1.compareTo(o2);
    };

    System.out.println(com2.compare(3, 2));
}

//语法格式六:当Lambda体只有一条语句时,return与大括号若有,都可以省略
@Test
public void test6(){
    Comparator<Integer> com1 = (o1,o2) -> {
            return o1.compareTo(o2);
    };

    System.out.println(com1.compare(1, 2));

    System.out.println("***********************");

    Comparator<Integer> com2 = (o1,o2) -> o1.compareTo(o2);

    System.out.println(com2.compare(3,2));
}

总结:

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

->右边:Lambda体应该使用一对{}包裹;如果Lambda体只有一条执行语句(可能是return语句),{}和return都可以省略

函数式接口

1. 函数式接口的使用说明

如果一个接口中,只声明了一个抽象方法,则此接口称为函数式接口;

@FunctionalInterface:函数式接口注解,检查是否是函数式接口

Lambda表达式的本质:作为函数式接口的实例;之前使用匿名实现类表示的现在都可以用Lambda表达式来写

2. Java8中关于Lambda表达式提供的4个基本的函数式接口

Java内置的4大核心函数式接口

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

3. 总结

  • 何时使用Lambda表达式?

    当需要对一个函数式接口实例化的时候,就可以使用Lambda表达式

  • 何时使用给定的函数式接口?

    开发中如需定义与JDK提供的函数式接口功能类似的函数式接口,即可考虑使用JDK现有的函数式接口

方法引用

1. 理解

本质上就是Lambda表达式,而Lambda表达式作为函数式接口的实例,所以方法引用也作为函数式接口的实例

2. 使用情景

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

3. 格式

类(或对象) :: 方法名

4. 具体使用

三种情况:

  1. 对象 :: 非静态方法

  2. 类 :: 静态方法

  3. 类 :: 非静态方法

例子:

// 情况一:对象 :: 实例方法
//Consumer中的void accept(T t)
//PrintStream中的void println(T t)
@Test
public void test1() {
   Consumer<String> con1 = str -> System.out.println(str);
   con1.accept("北京1");

   System.out.println("***********************");

   PrintStream ps = System.out;
   Consumer<String> con2 = ps :: println;
   con2.accept("北京2");
}

//Supplier中的T get()
//Employee中的String getName()
@Test
public void test2() {
   Employee emp = new Employee(1001,"Tom",23,5000);
   Supplier<String> sup1 = () -> emp.getName();
   System.out.println(sup1.get());

   System.out.println("***************");

   Supplier<String> sup2 = emp :: getName;
   System.out.println(sup2.get());
   
}

// 情况二:类 :: 静态方法
//Comparator中的int compare(T t1,T t2)
//Integer中的int compare(T t1,T t2)
@Test
public void test3() {
   Comparator<Integer> com1 = (t1,t2) -> Integer.compare(t1,t2);
   System.out.println(com1.compare(1,2));

   System.out.println("********************");

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

//Function中的R apply(T t)
//Math中的Long round(Double d)
@Test
public void test4() {
   Function<Double,Long> func = new Function<Double, Long>() {
      @Override
      public Long apply(Double aDouble) {
         return Math.round(aDouble);
      }
   };
   System.out.println(func.apply(1.2));

   System.out.println("**********************");

   Function<Double,Long> func1 = d -> Math.round(d);
   System.out.println(func1.apply(2.3));

   System.out.println("**********************");

   Function<Double,Long> func2 = Math :: round;
   System.out.println(func2.apply(3.4));

}

// 情况三:类 :: 实例方法 
// Comparator中的int compare(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"));

   System.out.println("**********************");

   Comparator<String> com2 = String :: compareTo;
   System.out.println(com2.compare("abc", "abd"));
}

//BiPredicate中的boolean test(T t1, T t2);
//String中的boolean t1.equals(t2)
@Test
public void test6() {
   BiPredicate<String,String> pre1 = (s1,s2) -> s1.equals(s2);
   System.out.println(pre1.test("abc", "abc"));

   System.out.println("***********************");

   BiPredicate<String,String> pre2 = String :: equals;
   System.out.println(pre2.test("abc","abc"));

}

// Function中的R apply(T t)
// Employee中的String getName();
@Test
public void test7() {
   Employee emp = new Employee(1001,"Jerry",23,6000);
   Function<Employee,String> func1 = e -> e.getName();
   System.out.println(func1.apply(emp));

   System.out.println("*************************");

   Function<Employee,String> fun2 = Employee :: getName;
   System.out.println(fun2.apply(emp));
}

5. 要求

  • 接口中的抽象方法的形参列表与返回值类型与方法引用的方法的形参列表和返回值类型相同(针对情况1和2)

  • 当函数式接口方法的第一个参数是需要引用方法的调用者,并且第二个参数是需要引用方法的参数(或无参数时):ClassName :: methodName(针对情况3)

构造器引用与数组引用

1. 构造器引用格式

类名 :: new

2. 构造器引用使用要求

和方法引用类似,函数式接口的抽象方法的形参列表和构造器的形参列表一致;抽象方法的返回值类型即为构造器所属的类

3. 构造器引用举例

//Supplier中的T get()
   //Employee的空参构造器:Employee()
   @Test
   public void test1(){
       Supplier<Employee> sup = new Supplier<Employee>() {
           @Override
           public Employee get() {
               return new Employee();
           }
       };
       sup.get();

       System.out.println("******************");

       Supplier<Employee> sup1 = () -> new Employee();
       sup1.get();

       System.out.println("******************");

       Supplier<Employee> sup2 = Employee::new;
       sup2.get();
}

//Function中的R apply(T t)
   @Test
   public void test2(){
       Function<Integer,Employee> func1 = id -> new Employee(id);
       Employee emp1 = func1.apply(1001);
       System.out.println(emp1);

       System.out.println("********************");

       Function<Integer,Employee> func2 = Employee :: new;
       Employee emp2 = func2.apply(1002);
       System.out.println(emp2);
   }

//BiFunction中的R apply(T t,U u)
   @Test
   public void test3(){
       BiFunction<Integer,String,Employee> func1 = (id,name) -> new Employee(id,name);
       Employee emp1 = func1.apply(1001, "Tom");
       System.out.println(emp1);

       System.out.println("*********************");

       BiFunction<Integer,String,Employee> func2 = Employee::new;
       Employee emp2 = func2.apply(1002, "Jerry");
       System.out.println(emp2);

   }

4. 数组引用格式

数组类型[] :: new

5. 数组引用举例

//数组引用
   //Function中的R apply(T t)
   @Test
   public void test4(){
       Function<Integer,String[]> func1 = length -> new String[length];
       String[] arr1 = func1.apply(10);
       System.out.println(Arrays.toString(arr1));

       System.out.println("********************");

       Function<Integer,String[]> func2 = String[] :: new;
       String[] arr2 = func2.apply(5);
       System.out.println(Arrays.toString(arr2));

   }

StreamAPI

1. StreamAPI的理解

  • Stream关注的是对数据的运算,集合关注的是数据的存储

  • 可对内存中的数据进行过滤、排序、映射、归约等操作

  • 注意点

    ① Stream自己不会存储元素

    ② Stream不会改变元对象,相反,它们会返回一个持有结果的新Stream

    ③ Stream操作是延迟执行的,这意味着它们会等到需要结果的时候才执行

2. 使用流程

  • 步骤一:Stream实例化

    //创建Stream方式一:通过集合
    @Test
    public void test1(){
        List<Employee> employees = EmployeeData.getEmployees();
        //default Stream<E> Stream():返回一个顺序流
        Stream<Employee> stream = employees.stream();
    
        //default Stream<E> parallelStream():返回一个并行流
        Stream<Employee> parallelStream = employees.parallelStream();
    }
    
    //创建Stream方式二:通过数组
    @Test
    public void test2(){
        int[] arr1 = new int[]{1,2,3,4,5};
        //调用Arrays类的static <T> Stream<T> stream (T[] array):返回一个流
        IntStream stream1 = Arrays.stream(arr1);
    
        Employee e1 = new Employee(1001,"Tom");
        Employee e2 = new Employee(1001,"Jerry");
        Employee[] arr2 = new Employee[]{e1,e2};
    
        Stream<Employee> stream2 = Arrays.stream(arr2);
    }
    
    //创建Stream方式三:通过Stream的of()
    @Test
    public void test3(){
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
    }
    
  • 步骤二:中间操作

    //1. 筛选与切片
    @Test
    public void test1(){
        List<Employee> list = EmployeeData.getEmployees();
        //filter(Predicate p) -- 接收Lambda,从流中排除某些元素
        Stream<Employee> stream = list.stream();
        //查询员工表中薪资大于7000的员工信息
        stream.filter(e -> e.getSalary() > 7000).forEach(System.out::println);
        System.out.println();
        //limit(long maxSize) -- 截断流,使其元素不超过给定数量
        list.stream().limit(3).forEach(System.out::println);
        System.out.println();
        //skip(long n) -- 跳过元素,返回一个扔掉了前n个元素的流,若流中元素不足n个,则返回一个空流
        list.stream().skip(3).forEach(System.out::println);
        System.out.println();
        //distinct() -- 筛选,通过流所生成元素的hashCode()和equals()去除重复元素
        list.add(new Employee(1010,"刘强东",40,8000));
        list.add(new Employee(1010,"刘强东",41,8000));
        list.add(new Employee(1010,"刘强东",40,8000));
        list.add(new Employee(1010,"刘强东",40,8000));
    
        list.stream().distinct().forEach(System.out::println);
    }
    
    //2. 映射
    @Test
    public void test2(){
        //map(Function f) -- 接受一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
        List<String> list = Arrays.asList("aa", "bb", "cc", "dd");
        list.stream().map(String::toUpperCase).forEach(System.out::println);
        //练习1:获取员工姓名长度大于3的员工的姓名
        List<Employee> employees = EmployeeData.getEmployees();
        Stream<String> namesStream = employees.stream().map(Employee::getName);
        namesStream.filter(name -> name.length() > 3).forEach(System.out::println);
        System.out.println();
        //练习2:
        Stream<Stream<Character>> streamStream = list.stream().map(StreamAPITest1::fromStringToStream);
        streamStream.forEach(s -> {
            s.forEach(System.out::println);
        });
        System.out.println();
        //flatMap(Function f) -- 接受一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
        Stream<Character> characterStream = list.stream().flatMap(StreamAPITest1::fromStringToStream);
        characterStream.forEach(System.out::println);
    }
    
    //将字符串中的多个字符构成的集合转换为对应的Stream实例
    public static Stream<Character> fromStringToStream(String str){
        ArrayList<Character> list = new ArrayList<>();
        for(Character c : str.toCharArray()){
            list.add(c);
        }
        return list.stream();
    }
    
    //3. 排序
    @Test
    public void test3(){
        //sorted -- 自然排序
        List<Integer> list = Arrays.asList(12, 43, 42, 56, 23, 5);
        list.stream().sorted().forEach(System.out::println);
    
        //sorted(Comparator com) -- 定制排序
        List<Employee> employees = EmployeeData.getEmployees();
        employees.stream().sorted( (e1,e2) -> {
            int ageValue =  Integer.compare(e1.getAge(),e2.getAge());
            if(ageValue != 0){
                return ageValue;
            }else{
                return Double.compare(e1.getSalary(), e2.getSalary());
            }
        }).forEach(System.out::println);
    }
    
  • 步骤三:终止操作

    //1. 匹配与查找
        @Test
        public void test1(){
            List<Employee> employees = EmployeeData.getEmployees();
            //allMatch(Predicate p) -- 检查是否匹配所有元素
            //练习:是否所有的员工年龄都大于18
            boolean allMatch = employees.stream().allMatch(e -> e.getAge() > 18);
            System.out.println(allMatch);
            //anyMatch(Predicate p) -- 检查是否至少匹配一个元素
            //练习:是否存在员工的工资大于10000
            boolean anyMatch = employees.stream().anyMatch(e -> e.getSalary() > 10000);
            System.out.println(anyMatch);
            //noneMatch(Predicate p) -- 检查是否没有匹配的元素
            //练习:是否存在员工姓“雷”
            boolean noneMatch = employees.stream().noneMatch(e -> e.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(e -> e.getSalary() > 5000).count();
            System.out.println(count);
            //max(Comparator c) -- 返回流中的最大值
            //练习:返回最高的工资
            Stream<Double> salaryStream = employees.stream().map(e -> e.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.forEach(System.out::println);
        }
    
        //2. 归约
        @Test
        public void test2(){
            //reduce(T identity,BinaryOperator) -- 可以将流中元素反复结合起来,得到一个值,返回T
            //练习1:计算1-10的自然数之和
            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) -- 可以将流中的元素反复结合起来,得到一个值,返回Optional<T>
            //练习2:计算公司所有员工工资总和
            List<Employee> employees = EmployeeData.getEmployees();
            Stream<Double> salaryStream = employees.stream().map(Employee::getSalary);
    //        Optional<Double> sumSalary = salaryStream.reduce(Double::sum);
            Optional<Double> sumSalary = salaryStream.reduce((d1,d2) -> d1 + d2);
            System.out.println(sumSalary);
        }
    
        //3. 收集
        @Test
        public void test3(){
            List<Employee> employees = EmployeeData.getEmployees();
            //collect(Collector c) -- 将流转换为其他形式,接收一个Collector接口的实现,用于给Stream中的元素做汇总的方法
            //练习1:查找工资大于6000的员工,结果返回为一个List或Set
            List<Employee> employeeList = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toList());
            employeeList.forEach(System.out::println);
            System.out.println();
            Set<Employee> employeeSet = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toSet());
            employeeSet.forEach(System.out::println);
    
        }
    
  • 注意点

    ① 一个中间操作链,对数据源的数据进行处理

    ② 一旦执行终止操作,就执行中间操作链,并产生结果,之后不会再被使用

Optional类的使用

1. 理解

为了在程序中避免出现空指针异常而创建的

2. 常用方法

/*
    Optional.of(T t):创建一个Optional实例,t必须非空
    Optional.empty():创建一个空的Optional实例
    Optional.ofNullable(T t):t可以为null
     */
    @Test
    public void test1(){
        Girl girl = new Girl();
        Optional<Girl> optionalGirl = Optional.of(girl);
    }

    

    @Test
    public void test4(){
        String str = "hello";

        Optional<String> op1 = Optional.of(str);
        //get():返回Optional封装的数据,若为空则报错;通常与of()搭配使用
        String str1 = op1.get();
        System.out.println(str1);
    }
	
	/*
    ofNullable(T t):创建一个Optional实例,t可以为空
    orElse(T t1):如果当前Optional内部封装的t非空,则返回内部的t;如果t为空,则返回orElse()方法中的参数t1
     */
    @Test
    public void test5(){
        String str = "北京";
        str = null;

        Optional<String> op1 = Optional.ofNullable(str);

        String str1 = op1.orElse("上海");

        System.out.println(str1);
    }

3. 典型练习

@Test
    public void test2(){
        Girl girl = new Girl();
//        girl = null;
        Optional<Girl> optionalGirl = Optional.ofNullable(girl);
        System.out.println(optionalGirl);

        Girl girl1 = optionalGirl.orElse(new Girl("三上悠亚"));
        System.out.println(girl1);
    }

    public String getGirlName(Boy boy){
        return boy.getGirl().getName();
    }

    //优化方法
    public String getGirlName1(Boy boy){
//        if(boy != null){
//            Girl girl = boy.getGirl();
//            if(girl != null){
//                return girl.getName();
//            }
//        }
//        return null;

        //使用Optional类
        Optional<Boy> optionalBoy = Optional.ofNullable(boy);
        //此时boy1一定非空
        Boy boy1 = optionalBoy.orElse(new Boy(new Girl("桥本有菜")));

        Girl girl = boy1.getGirl();
        Optional<Girl> optionalGirl = Optional.ofNullable(girl);
        //此时girl1一定非空
        Girl girl1 = optionalGirl.orElse(new Girl("天使萌"));

        return girl1.getName();
    }

    @Test
    public void test3(){
        Boy boy = null;
        boy = new Boy();
        boy = new Boy(new Girl("明里紬"));
//        String girlName = getGirlName(boy);
        String girlName = getGirlName1(boy);
        System.out.println(girlName);
    }
posted on 2021-02-20 16:36  来点番茄酱  阅读(74)  评论(0)    收藏  举报