Java8新特性学习

Source

学习代码

1.主要内容

1.Lambda表达式
2. 函数式接口
3. 方法引用于构造器引用
4. Stream API
5. 接口中的默认方法与静态方法
6. 新时间日期API
7. 重复注解

入门案例:根据条件获取员工信息

//员工表
public class Employee {
    private String name;
    private int age;
    private double salary;
    ....
}
//员工信息
List<Employee> emplpyees = Arrays.asList(
    new Employee("张三", 14, 5444.42),
    new Employee("李四", 39, 3434.15),
    new Employee("王五", 31, 5425.55),
    new Employee("赵六", 11, 2544.42), 
    new Employee("陈七", 98, 944.42));
1.传统方式
    @Test
    public void test3() {
        List<Employee> emps = filterEmployees(emplpyees);
        System.out.println(emps);
    }
    // 获取当前员工中年龄大于35的员工
    public List<Employee> filterEmployees(List<Employee> list) {
        List<Employee> emps = new ArrayList<>();
        for (Employee emp : list) {
            if (emp.getAge() >= 35) {
                emps.add(emp);
            }
        }
        return emps;
    }

如果条件改变呢?比如,改成想要工资大于5000的员工信息.传统方式,拷贝代码,修改if代码
优化方式一:设计模式,策略设计模式
定义一个借口,根据条件创建实现类

//定义一个接口
public interface MyProdicate<T> {
    public boolean test(T t);
}
//实现类,
public class FilterEmployeeByAge implements MyProdicate<Employee>{
    @Override
    public boolean test(Employee t) {
        return t.getAge()>=35;
    }
}
//判断
public List<Employee> filterEmployees(List<Employee> list, MyProdicate<Employee> mp) {
        List<Employee> emps = new ArrayList<>();
        for (Employee emp : list) {
            if (mp.test(emp)) {
                emps.add(emp);
            }
        }
        return emps;
}
//测试
@Test
    public void test4() {
        List<Employee> emps = filterEmployees(emplpyees, new FilterEmployeeByAge());
        System.out.println(emps);
    }

不过,这种方式中,每加一个条件,就要加一个实现类,不好,在此基础上进行优化
匿名内部类

// 优化方式二,匿名内部类
    public void test5() {
        List<Employee> emps = filterEmployees(emplpyees, new MyProdicate<Employee>() {
            @Override
            public boolean test(Employee t) {
                return t.getSalary() >= 5000;
            }
        });
        System.out.println(emps);
    }

匿名内部类每次使用,还是要写多余代码,可以继续优化
lambda表达式

// 优化方式三:lambda]
    @Test
    public void test6() {
        List<Employee> emps = filterEmployees(emplpyees, (e) -> e.getSalary() < 5000);
        emps.forEach(System.out::println);
    }

如果没有接口.怎么优化Stream API

// 优化方式四,只有emplpyees,Stream API
@Test
public void test7() {
    emplpyees.stream().filter((e) -> e.getSalary() < 5000).forEach(System.out::println);
}

2.Lambda表达式

1.匿名函数

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

2.语法格式

1.lambda表达式的基础语法

java8中引入新的操作符"->",箭头操作符/lambda操作符 箭头操作符将lambda 表达式拆分成两部分:
左侧:lambda的参数列表 右侧:lambda中所需要执行的功能,即lambda体

• 语法格式1:无参数,无返回值 :()->具体功能
• 语法格式2:有一个参数,无返回值:(x)->具体功能
• 语法格式3:若只有一个参数,参数小括号可以省略不写:x->具体功能
• 语法格式4:有两个以上的参数,lambda体中有多条语句,有返回值:
• 语法格式5:有两个参数,lambda体只有一条语句,有返回值,{}和return可以省略
• 语法格式6:lambda表达式的数据类型可以省略不写,因为jvm的编译器可以通过上下文推断出数据类型,即类型推断
  口诀:
• 左右遇一括号省
• 左侧推断类型省
package cn.zj.lambda;
import java.util.Comparator;
import java.util.function.Consumer;
import org.junit.jupiter.api.Test;
/**
 * 1.lambda表达式的基础语法,java8中引入新的操作符"->",箭头操作符/lambda操作符 箭头操作符将lambda 表达式拆分成两部分:
 * 左侧:lambda的参数列表 右侧:lambda中所需要执行的功能,即lambda体
 *
 * 语法格式1:无参数,无返回值 :()->具体功能
 * 语法格式2:有一个参数,无返回值:(x)->具体功能
 * 语法格式3:若只有一个参数,参数小括号可以省略不写:x->具体功能
 * 语法格式4:有两个以上的参数,lambda体中有多条语句,有返回值:
 * 语法格式5:有两个参数,lambda体只有一条语句,有返回值,{}和return可以省略
 * 语法格式6:lambda表达式的数据类型可以省略不写,因为jvm的编译器可以通过上下文推断出数据类型,即类型推断
 * 
 * 口诀:
 * 左右遇一括号省
 * 左侧推断类型省
 * 
 */
public class TestLambda2 {
    // 1.无参数,无返回值
    @Test
    public void test1() {
        int i = 1;// 在7之前,内部类调用同级别变量参数,需要加final修饰符,8以后不加,底层默认加了
        Runnable r = () -> System.out.println("hello Lambda" + i);
        r.run();
    }
    
    @Test
    public void test2() {
        Consumer<String> c=(x)->System.out.println(x);
        c.accept("lambda");
        
    }
    
    @Test
    public void test3() {
        Consumer<String> c=x->System.out.println(x);
        c.accept("lambda");
        
    }
    
    
    @Test
    public void test4() {
        Comparator<Integer> com=(x,y)->{
            System.out.println("lambda多条语句");
            return Integer.compare(x, y);
        };
        int compare = com.compare(11, 33);
        System.out.println(compare);
    }
    
    @Test
    public void test5() {
        Comparator<Integer> com=(x,y)->Integer.compare(x, y);
        int compare = com.compare(11, 33);
        System.out.println(compare);
    }
}

2.lambda表达式需要函数式接口的支持函数式接口

函数式接口:只有一个抽象方法的接口

public interface MyProdicate<T> {
    public boolean test(T t);
}

可以使用注解@FunctionalInterface修饰,检查是否是函数式接口

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

3.练习

3.1.调用Collections.sort()方法,通过定制排序比较两个Employee(先按年龄比,年龄相同按姓名比),使用lambda作为参数传递

  @Test
  public void test1() {
      Collections.sort(emps, (e1, e2) -> {
          if (e1.getAge() == e2.getAge()) {
              return e1.getName().compareTo(e2.getName());
          } else {
              return Integer.compare(e1.getAge(), e2.getAge());
          }
      });
      emps.forEach(System.out::println);
  }

3.2.①声明函数式接口,接口中声明抽象方法,public String getValue(String str);
②声明类LambdaTest,类中编写方法使用接口作为参数,将一个字符串转为大写并返回
③再将字符串的第2和第4个索引位置进行截取

@FunctionalInterface
public interface MyFunction {
 public String getValue(String str);
}
@Test
 public void test2() {
     String str = strHandler("hellolambda", (s) -> s.toUpperCase());
     str = strHandler(str, (s) -> s.substring(2, 5));
     System.out.println(str);
 }
 public String strHandler(String str, MyFunction mf) {
     return mf.getValue(str);
 }

3.3.①声明一个带两个泛型的函数式接口,泛型类型为<T,R>,T为参数,R为返回值,
②接口中声明对应方法
③在LambdaTest类中声明方法,使用接口作为参数,计算两个long型参数的和
④再计算两个long型参数的乘积

@FunctionalInterface
public interface MyFunction2<T,R> {
 public R getValue(T t1,T t2);
}
@Test
 public void test3() {
     Long l1=longHandler(4121754L,55524L,(x,y)->x+y);
     Long l2=longHandler(4121754L,55524L,(x,y)->x*y);
     System.out.println(l1);
     System.out.println(l2);
 }
 
 public Long longHandler(Long l1, long l2, MyFunction2<Long,Long> mf) {
     return  mf.getValue(l1, l2);
 }

3.java内置四大类型接口

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

1.Consumer 消费型接口

//消费型接口Consumer
    @Test
    public void test1() {
        happy(10000,(m)->System.out.println("吃消费"+m));
    }
    
    public void happy(double money,Consumer<Double> con) {
        con.accept(money);
    }

2.Suppplier供给型接口 T get()

//供给型接口Supplier
    @Test
    public void test2() {
        List<Integer>  list=getNumList(10,()->(int)(Math.random()*100));
        System.out.println(list);
    }
    //产生一些整数,并放入集合中
    public List<Integer> getNumList(int num,Supplier<Integer> sup){
        List<Integer> list=new ArrayList<>();
        for(int i=0;i<num;i++) {
            list.add(sup.get());
        }
        return list;
    }

3.Function<T,R>函数型接口 R apply(T t)

//函数型接口Function
    @Test
    public void test3() {
        String str = strHandler(" 韩梅梅要嫁给王五了 ", (s)->s.trim());
        System.out.println(str);
    }
    //处理字符串
    public String strHandler(String str,Function<String,String> fun) {
        return fun.apply(str);
    }

4.Predicate断定型接口

   // 断定型接口Predicate
    @Test
    public void test4() {
        List<String> list = Arrays.asList("hello","world","lambda","ok","good");
        List<String> strList = filterStr(list, (s)->s.length()>4);
        System.out.println(strList);
    }
    // 将满足条件的字符串加到集合中
    public List<String> filterStr(List<String> list, Predicate<String> pre) {
        List<String> strList = new ArrayList<>();
        for (String str : list) {
            if (pre.test(str))
                strList.add(str);
        }
        return strList;
    }

其它接口
在这里插入图片描述

3.方法引用与构造器引用

1.方法引用

如果lambda体中的内容已经有方法实现了,可以使用"方法引用"(可以理解为lambda的另一种表现形式)

1.对象::实例方法名

//对象::实例方法名
@Test
public void test1() {
    Consumer<String> con=(x)->System.out.println(x);
    con.accept("哇咔咔");
    //方法引用:接口中抽象方法的参数和返回值类型,需要与要引用的方法的参数和返回值类型一样
    PrintStream ps=System.out;
    Consumer<String> con1=ps::println;
    con1.accept("把金三角");
    Consumer<String> con2=System.out::println;
    con2.accept("asasa");
}

@Test
public void test2() {
    Employee emp=new Employee("张三", 11, 124.55);
    Supplier<String> sup=()->emp.getName();
    String str = sup.get();
    System.out.println(str);
    
    //方法引用
    Supplier<Integer> sup1=emp::getAge;
    Integer age = sup1.get();
    System.out.println(age);
}

2.类::静态方法名

//类::静态方法

 @Test
    public void test3() {
        Comparator<Integer> com=(x,y)->Integer.compare(x, y);
        
        Comparator<Integer> com1=Integer::compare;
        int compare = com1.compare(11, 12);
        System.out.println(compare);
    }

3.类::实例方法名

//类::实例方法名
    @Test
    public void test4() {
        BiPredicate<String, String>bp=(x,y)->x.equals(y);
        
        //注意,第一个参数是实例方法的调用者,第二个参数是实例方法的参数
        BiPredicate<String, String>bp1=String::equals;
        boolean test = bp1.test("ss", "ss");
        System.out.println(test);
    }

4.注意

注意:
lambda体中调用方法的参数列表与返回值类型,要与函数式接口中抽象方法的参数列表和返回值类型保持一致

lambda参数列表中的第一个参数是实例方法的参数者,第二个参数是实例方法的参数时可以使用类::实例方法名

2.构造器引用

格式:ClassName::new
//无参
    @Test
    public void test1() {
        Supplier<Employee> sup=()->new Employee();
        //构造器引用,无参
        Supplier<Employee> sup1=Employee::new;
        
    }
    //有参数
    @Test
    public void test2() {
        Function<Integer, Employee>fun=(x)->new Employee(x);
        
        Function<Integer, Employee>fun1=Employee::new;
        Employee emp = fun1.apply(11);
        System.out.println(emp);
        
        //两个参数
        BiFunction<Integer,Integer, Employee>bf=Employee::new;
        Employee emp2 = bf.apply(11,21);
        System.out.println(emp2);
    }

注意:需要调用的构造器的参数列表要与函数式接口中抽象方法的参数列表保持一致

3.数组引用

Type::new


    @Test
    public void test1() {
        Function<Integer, String[]> fun=(x)->new String[x];
        String[] strArr = fun.apply(12);
        System.out.println(strArr.length);
        
        Function<Integer, String[]> fun1=String[]::new;
        String[] strArr2 = fun1.apply(10);
        System.out.println(strArr2.length);
    }

4.Stream API

1.Stream 概念

是数据渠道,用于操作数据源(集合.数组等)所生成的元素序列. “集合将的事数据,流讲的是计算” 注意:
ⅰ. Stream自己不会存储元素
ⅱ. Stream不会改变源对象.相反,他们会返回一个持有结果的新Stream
ⅲ. Stream操作是延迟执行的.这意味着他们会等到需要结果的时候才执行

2.操作步骤

1.创建 Stream

一个数据源(集合,数组等)获取一个流

  1. 通过Collection系列集合提供的stream()或paralleStream()
  2. 通过Arrays中的静态方法stream()获取数组流
  3. 通过Stream类中的静态方法of()
  4. 创建无限流
// 创建Stream
  @Test
    public void test1() {
        // 1.通过Collection系列集合提供的stream()或paralleStream()
        List<String> list = new ArrayList<>();
        Stream<String> stream = list.stream();
        
        //2.通过Arrays中的静态方法stream()获取数组流
        Employee[] emps=new Employee[10];
        Stream<Employee> stream2 = Arrays.stream(emps);
        
        //3.通过Stream类中的静态方法of()
        Stream<String> stream3 = Stream.of("aa","bb","cc");
        
        //4.创建无限流
        //1)迭代
        Stream<Integer> stream4 = Stream.iterate(0, (x)->x+2);
        //stream4.limit(10).forEach(System.out::println);
        
        //2)生成
        Stream<Double> stream5 = Stream.generate(()->Math.random());
        //limit(10)中间操作
        //forEach 终止操作
        stream5.limit(10).forEach(System.out::println);//终止操作
    }

2.中间操作

一个中间操作链,对数据源的数据进行处理
多个中间操作可以连接起来形成一个流水线,除非流水线上出发终止操作,否则中间操作不会执行任何的处理.而在终止操作时一次性全部处理,称为"惰性求值"

1.筛选与切片
方法 描述
filter(Predicate p) 接收Lambda,从流中排除某些元素
distinct() 筛选,通过流所生成元素的hashCode()和equals()去除重复元素
limit(long maxSize) 截断流,使其元素不超过给定数量
skip(long n) 跳过元素,返回一个扔掉了前n个元素的流.若流中元素不足n个,则返回一个空流.与limit(n)互补
public class TestStreamAPI02 {
    
    List<Employee> emps = Arrays.asList(
            new Employee("张三", 14, 5444.42),
            new Employee("李四", 39, 3434.15),
            new Employee("李四", 39, 3434.15),
            new Employee("王五", 31, 5425.55),
            new Employee("赵六", 31, 2544.42), 
            new Employee("陈七", 98, 944.42));
    //中间操作
    /*
     * 筛选与切片
     * filter-接受lambda,从流中排除某些元素
     * limit-截断流,使其元素不能超过给定数量
     * skip(n) -跳过元素,返回一个扔掉了前n个元素的流,若流中元素不足n个,则返回一个空流.与limit(n)互补
     * distinct-筛选,通过流所生成元素的hashCode()和equals()去除重复元素
     * 
     */
    //distinct
    @Test
    public void test5() {
        emps.stream().filter((e)->e.getSalary()>3000).distinct().forEach(System.out::println);
    
    }
    
    //skip
    @Test
    public void test4() {
        emps.stream().filter((e)->e.getSalary()>5000).skip(1).forEach(System.out::println);
    
    }
    
    //limit,得到对应的数据后,会停止迭代,短路
    @Test
    public void test3() {
        emps.stream().filter((e)->e.getSalary()>5000).limit(2).forEach(System.out::println);
    }
    
    
    //内部迭代:迭代操作由Stream API完成
    //filter
    @Test
    public void test1() {
        //中间操作,不会执行任何操作
        Stream<Employee> stream = emps.stream().filter((e)->e.getAge()>35);
        //终止操作,一次性执行全部内容,"惰性求值"
        stream.forEach(System.out::println);
    }
    
    //外部迭代
    @Test
    public void test2() {
        Iterator<Employee> it = emps.iterator();
        while(it.hasNext()) {
            System.out.println(it.next());
        }
    }
    
}
2.映射

• map:接收 Lambda ,将元素转换为其他形式或提取信息;接受一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
• flatMap:接收一个函数作为参数,将流中每一个值都换成另一个流,然后把所有流重新连接成一个流

public class TestStreamAPI3 {
    
    List<Employee> emps = Arrays.asList(
            new Employee("张三", 14, 5444.42),
            new Employee("李四", 39, 3434.15),
            new Employee("李四", 39, 3434.15),
            new Employee("王五", 31, 5425.55),
            new Employee("赵六", 31, 2544.42), 
            new Employee("陈七", 98, 944.42));
    //中间操作
    //映射
    //map-接收Lambda,将元素转换成其它形式或提取信息,接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
    //flatMap-接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
    
    
    //flatMap
    @Test
    public void test2() {
        List<String> list=Arrays.asList("aaa","bbb","ccc","ddd","eee");
        list.stream().flatMap(TestStreamAPI3::filterCharacter).forEach(System.out::println);
    }
    
    public static Stream<Character> filterCharacter(String str){
        List<Character> list = new ArrayList<>();
        for (char c : str.toCharArray()) {
            list.add(c);
        }
        return list.stream();
    }
    
    //map
    @Test
    public void test1() {
        List<String> list=Arrays.asList("aaa","bbb","ccc","ddd","eee");
        list.stream().map((x)->x.toUpperCase()).forEach(System.out::println);
        System.out.println("-------------------------------");
        emps.stream().map((x)->x.getName()).forEach(System.out::println);
        System.out.println("-------------------------------");
        Stream<Stream<Character>> stream=list.stream().map(TestStreamAPI3::filterCharacter);
        stream.forEach((sm)->{
            sm.forEach(System.out::println);
        });
    }
    
    
}
3.排序

• sorted():自然排序
• sorted(Comparator c):定制排序

//中间操作
    //排序
    //sorted():自然排序
    //sorted(Comparator c):定制排序
    //sorted(Comparator c)
        @Test
        public void test2() {
            emps.stream().sorted((x,y)->{
                if(x.getAge()==y.getAge()) {
                    return x.getName().compareTo(y.getName());
                }else {
                    return Integer.compare(x.getAge(), y.getAge());
                }
            }).forEach(System.out::println);
        }
    
    //sorted
    @Test
    public void test1() {
        List<String> list=Arrays.asList("aaa","ddd","bbb","eee","ccc");
        list.stream().sorted().forEach(System.out::println);
    }

3.终止操作(终端操作)

一个终止操作,执行中间操作链,并产生结果

1.查找与匹配
• allMatch:检查是否匹配所有元素
• anyMatch:检查是否至少匹配一个元素
• noneMatch:检查是否没有匹配所有元素
• findFirst:返回第一个元素
• findAny:返回当前流中的任意元素
• count:返回流中元素的总个数
• max:返回流中最大值
• min:返回流中最小值
public class TestStreamAPI4 {
    List<Employee> emps = Arrays.asList(
            new Employee(1,"张三", 14, 5444.42,Status.FREE),
            new Employee(2,"李四", 39, 3434.15,Status.BUSY),
            new Employee(3,"李四", 39, 3434.15,Status.VOCATION), 
            new Employee(4,"王五", 31, 5425.55,Status.FREE), 
            new Employee(5,"赵六", 31, 2544.42,Status.BUSY),
            new Employee(6,"陈七", 98, 944.42,Status.BUSY));
    // 终止操作
    // 查找与匹配
    // allMatch:检查是否匹配所有元素
    // anyMatch:检查是否至少匹配一个元素
    // noneMatch:检查是否没有匹配所有元素
    // findFirst:返回第一个元素
    // findAny:返回当前流中的任意元素
    // count:返回流中元素的总个数
    // max:返回流中最大值
    // min:返回流中最小值
    
    //min
    @Test
    public void test8() {
        /*
         * Optional<Employee> op =
         * emps.stream().min((x,y)->Double.compare(x.getSalary(), y.getSalary()));
         * System.out.println(op.get().getSalary());
         */
        Optional<Double> op = emps.stream().map(Employee::getSalary).min(Double::compare);
        System.out.println(op.get());
    }
    //max
    @Test
    public void test7() {
        Optional<Employee> op = emps.stream().max((x,y)->Double.compare(x.getSalary(), y.getSalary()));
        System.out.println(op.get());
    }
    
    //count
    @Test
    public void test6() {
        long count = emps.stream().count();
        System.out.println(count);
    }
    
    
    //findAny
    @Test
    public void test5() {
         Optional<Employee> op = emps.stream().filter((e)->e.getStatus().equals(Status.FREE)).findAny();
         System.out.println(op.get());
    }
    
    //findFirst
    @Test
    public void test4() {
        Optional<Employee> op = emps.stream().sorted((x,y)->Double.compare(x.getSalary(), y.getSalary())).findFirst();
        //op.orElse(null)
        System.out.println(op.get());
    }
    
    //noneMatch
    @Test
    public void test3() {
        boolean b1 = emps.stream().noneMatch((e)->e.getStatus().equals(Status.BUSY));
        System.out.println(b1);
    }
    
    //anyMatch
    @Test
    public void test2() {
        boolean b1 = emps.stream().anyMatch((e)->e.getStatus().equals(Status.BUSY));
        System.out.println(b1);
    }
    //allMatch
    @Test
    public void test1() {
        boolean b1 = emps.stream().allMatch((e)->e.getStatus().equals(Status.BUSY));
        System.out.println(b1);
    }
}
2.规约与收集

!image

• 归约:reduce(T identity, BinaryOperator) / reduce(BinaryOperator) 可以将流中的数据反复结合起来,得到一个值,通常和map合用,map-reduce模式
• 收集:collect 将流转换成其他形式;接收一个 Collector 接口的实现,用于给流中元素做汇总的方法
public class TestStreamAPI6 {
    List<Employee> emps = Arrays.asList(new Employee(1, "张三", 14, 5444.42, Status.FREE),
            new Employee(2, "李四", 39, 3434.15, Status.BUSY), new Employee(3, "李四", 39, 3434.15, Status.VOCATION),
            new Employee(4, "王五", 31, 5425.55, Status.FREE), new Employee(5, "赵六", 31, 2544.42, Status.BUSY),
            new Employee(6, "陈七", 98, 944.42, Status.BUSY));
    // 终止操作
    // 收集:collect 将流转换成其他形式;接收一个 Collector 接口的实现,用于给流中元素做汇总的方法
    //连接
    @Test
    public void test9() {
        String string = emps.stream().map(Employee::getName).collect(Collectors.joining(","));
        System.out.println(string);
    }
    @Test
    public void test8() {
        DoubleSummaryStatistics collect = emps.stream().collect(Collectors.summarizingDouble(Employee::getSalary));
        System.out.println(collect);
    }
    // 分区/分片
    @Test
    public void test7() {
        Map<Boolean, List<Employee>> map = emps.stream().collect(Collectors.partitioningBy((e) -> e.getAge() > 30));
        System.out.println(map);
    }
    // 多级分组
    @Test
    public void test6() {
        // 按状态分组
        Map<Status, Map<Object, List<Employee>>> map = emps.stream()
                .collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy((e) -> {
                    if (e.getAge() > 30) {
                        return "青年";
                    } else {
                        return "老家伙";
                    }
                })));
        System.out.println(map);
    }
    // 分组
    @Test
    public void test5() {
        // 按状态分组
        Map<Status, List<Employee>> map = emps.stream().collect(Collectors.groupingBy(Employee::getStatus));
        System.out.println(map);
    }
    @Test
    public void test4() {
        // 总数
        Long collect = emps.stream().collect(Collectors.counting());
        System.out.println("总数:" + collect);
        // 工资平均值
        Double averag = emps.stream().collect(Collectors.averagingDouble(Employee::getSalary));
        System.out.println("工资平均值:" + averag);
        // 工资总和
        Double sumSalary = emps.stream().collect(Collectors.summingDouble(Employee::getSalary));
        System.out.println("工资总和:" + sumSalary);
        // 工资最大值
        Optional<Employee> op = emps.stream()
                .collect(Collectors.maxBy((x, y) -> Double.compare(x.getSalary(), y.getSalary())));
        System.out.println("最大工资:" + op.get());
        // 工资最小值
        Optional<Double> op2 = emps.stream().map(Employee::getSalary).collect(Collectors.minBy(Double::compare));
        System.out.println("最小工资:" + op2.get());
    }
    @Test
    public void test3() {
        // 收集所有的员工姓名
        // list中
        List<String> list = emps.stream().map(Employee::getName).collect(Collectors.toList());
        System.out.println(list);
        System.out.println("--------");
        // set中
        Set<String> set = emps.stream().map(Employee::getName).collect(Collectors.toSet());
        System.out.println(set);
        System.out.println("--------");
        // 其它集合中
        HashSet<String> hashSet = emps.stream().map(Employee::getName).collect(Collectors.toCollection(HashSet::new));
        System.out.println(hashSet);
    }
    // 归约:reduce(T identity, BinaryOperator) / reduce(BinaryOperator)
    // 可以将流中的数据反复结合起来,得到一个值
    @Test
    public void test2() {
        // 计算工资总和
        Optional<Double> op = emps.stream().map((e) -> e.getSalary()).reduce(Double::sum);
        System.out.println(op.get());
    }
    @Test
    public void test1() {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        Integer sum = list.stream().reduce(0, (x, y) -> x + y);
        System.out.println(sum);
    }
}

5.并行流与串行流

!image.png

• 并行流:就是把一个内容分成几个数据块,并用不同的线程分别处理每个数据块的流
• Java 8 中将并行进行了优化,我们可以很容易的对数据进行操作;Stream API 可以声明性地通过 parallel() 与 sequential() 在并行流与串行流之间切换

fork/join

public class ForkJoinCalculate extends RecursiveTask<Long>{
    private static final long serialVersionUID = 1L;
    private long start;
    private long end;
    private static final long THRESHPLD =1000;
    /**
     * @param start
     * @param end
     */
    public ForkJoinCalculate(long start, long end) {
        super();
        this.start = start;
        this.end = end;
    }
    @Override
    protected Long compute() {
        long length=end-start;
        if(length<=THRESHPLD) {
            long sum=0;
            for(long i=start;i<=end;i++) {
                sum+=i;
            }
            return sum;
        }else {
            long middle = (start + end) / 2;
            ForkJoinCalculate left=new ForkJoinCalculate(start, middle);
            left.fork();//拆分成子任务,同时加入线程队列
            
            ForkJoinCalculate right=new ForkJoinCalculate(middle+1, end);
            right.fork();
            
            return left.join()+right.join();
            
        }
        
    }
    
}
//fork/join  数值越大,越能体现性能
    @Test
    public void test1() {
        Instant start = Instant.now();
        ForkJoinPool pool = new ForkJoinPool();
        ForkJoinTask<Long> task=new ForkJoinCalculate(0, 100000000);
        Long sum = pool.invoke(task);
        System.out.println(sum);
        Instant end = Instant.now();
        System.out.println("耗费时间:"+Duration.between(start, end).toMillis()+"毫秒");//70
    }
    
    //普通for循环
    @Test
    public void test2() {
        Instant start = Instant.now();
        long sum=0;
        for(int i=0;i<=100000000;i++) {
            sum+=i;
        }
        System.out.println(sum);
        Instant end = Instant.now();
        System.out.println("耗费时间:"+Duration.between(start, end).toMillis()+"毫秒");//30
    }

java8并行流 parallel 底层 fork/join

//java8并行流 parallel()
@Test
public void test3(){
    Instant start = Instant.now();
    OptionalLong op = LongStream.rangeClosed(0, 1000000000L)
            .parallel()
            .reduce(Long::sum);
    System.out.println(op.getAsLong());
    Instant end = Instant.now();
    System.out.println("耗费时间:"+Duration.between(start, end).toMillis()+"毫秒");//49->257毫秒
}

6.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
public class TestOptiona {
    /*
     * 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
     * 
     */
    @Test
    public void test8() {
        Optional<Employee> op = Optional.ofNullable(new Employee(1, "张三", 110, 55440.14, Status.BUSY));
        Optional<String> flatMap = op.flatMap((e)->Optional.of(e.getName()));
        System.out.println(flatMap.get());
    }
    
    @Test
    public void test7() {
        Optional<Employee> op = Optional.ofNullable(new Employee(1, "张三", 110, 55440.14, Status.BUSY));
        Optional<String> map = op.map((e)->e.getName());
        System.out.println(map.get());
    }
    
    @Test
    public void test6() {
        Optional<Employee> op = Optional.ofNullable(new Employee());
        Employee emp=op.orElseGet(()-> new Employee(1, "张三", 110, 55440.14, Status.BUSY));
        System.out.println(emp);
    }
    @Test
    public void test5() {
    
        Optional<Employee> op = Optional.ofNullable(new Employee());
        Employee emp=op.orElse(new Employee(1, "张三", 110, 55440.14, Status.BUSY));//避免空指针异常
        System.out.println(emp);
    }
    
    
    @Test
    public void test4() {
        //ofNullable是empty和of的综合
        Optional<Employee> op = Optional.ofNullable(new Employee());
        if(op.isPresent()) {
            Employee emp = op.get();//空指针报错位置
            System.out.println(emp);
        }
        
    }
    
    @Test
    public void test3() {
        //ofNullable是empty和of的综合
        Optional<Employee> op = Optional.ofNullable(new Employee());
    
        Employee emp = op.get();//空指针报错位置
        System.out.println(emp);
    }
    
    @Test
    public void test2() {
        Optional<Employee> op = Optional.empty();
        Employee emp = op.get();//空指针报错位置
        System.out.println(emp);
    }
    @Test
    public void test1() {
        Optional<Employee> op = Optional.of(new Employee());//传入null空指针报错位置
        Employee emp = op.get();
        System.out.println(emp);
    }
}

7.接口中的默认方法与静态方法

1.默认方法

public interface MyFun {
    default String getName() {
        return "libo";
    }
    default Integer getAge() {
        return 22;
    }
}
public class MyClass {
    public String getName() {
        return "MyClass类";
    }
}
public class SubClass extends MyClass implements MyFun{
}
public static void main(String[] args) {
        SubClass sc=new SubClass();
        String name = sc.getName();//类优先原则
        System.out.println(name);
    }
public interface MyInterfaces {
    default String getName() {
        return "MyInterfaces接口";
    }
}
public class SubClass2 implements MyFun,MyInterfaces{
    //实现多接口,并有同方法,必须实现该方法
    @Override
    public String getName() {
        return MyFun.super.getName();
    }
}

2.静态方法

public interface MyInterfaces {
    default String getName() {
        return "MyInterfaces接口";
    }
    
    public static void show() {
        System.out.println("接口中的静态方法");
    }
}
MyInterfaces.show();

8.新时间API

1.传统时间存在安全问题

    public static void main(String[] args) throws Exception {
        //存在线程安全问题
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
        Callable<Date> task = () -> sdf.parse("20210111");
        ExecutorService pool = Executors.newFixedThreadPool(10);
        ArrayList<Future<Date>> result = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            result.add(pool.submit(task));
        }
        for (Future<Date> future : result) {
            System.out.println(future.get());
        }
        pool.shutdown();
    }
//线程安全
public class DateFormatThreadLocal {
    private static final ThreadLocal<DateFormat> df = ThreadLocal
            .withInitial(() -> new SimpleDateFormat("yyyyMMdd"));
    public static Date convert(String source) throws ParseException {
        return df.get().parse(source);
    }
}
public static void main(String[] args) throws Exception {
        //存在线程安全问题
        //SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
        //Callable<Date> task = () -> sdf.parse("20210111");
        
        Callable<Date> task =() ->DateFormatThreadLocal.convert("20181212");
        ExecutorService pool = Executors.newFixedThreadPool(10);
        ArrayList<Future<Date>> result = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            result.add(pool.submit(task));
        }
        for (Future<Date> future : result) {
            System.out.println(future.get());
        }
        pool.shutdown();
    }
    public static void main(String[] args) throws Exception {
        //java8新时间
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMdd");
        Callable<LocalDate> task =() ->LocalDate.parse("20181212",dtf);
        ExecutorService pool = Executors.newFixedThreadPool(10);
        ArrayList<Future<LocalDate>> result = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            result.add(pool.submit(task));
        }
        for (Future<LocalDate> future : result) {
            System.out.println(future.get());
        }
        pool.shutdown();
    }

2.本地时间

image.png
常用方法:

方法名 返回值类型 解释
now( ) static LocalDateTime 从默认时区的系统时钟获取当前日期
of(int year, int month, int dayOfMonth, int hour, int minute, int second) static LocalDateTime 从年,月,日,小时,分钟和秒获得 LocalDateTime的实例,将纳秒设置为零
plus(long amountToAdd, TemporalUnit unit) LocalDateTime 返回此日期时间的副本,并添加指定的数量
get(TemporalField field) int 从此日期时间获取指定字段的值为 int
public class TestLocalDateTime {
    //1.LocalDate  LocalTime  LocalDateTime 
    
    @Test
    public void  test1() {
        //获取当前时间日期 now
        LocalDateTime ldt=LocalDateTime.now();
        System.out.println(ldt);
        
        //指定时间日期 of
        LocalDateTime ldt2 = LocalDateTime.of(2021, 8, 14, 16, 24, 33);
        System.out.println(ldt2);
        
      //加 plus
        LocalDateTime ldt3 = ldt2.plusYears(2);
        System.out.println("加两年:"+ldt3);
        //减 minus
        LocalDateTime ldt4 = ldt2.minusMonths(3);
        System.out.println("减三个月:"+ldt4);
        //获取指定的你年月日时分秒... get
        System.out.println("年"+ldt2.getDayOfYear());
        System.out.println("时"+ldt2.getHour());
        System.out.println("秒"+ldt2.getSecond());
    }
}

3.时间戳

Instant:以 Unix 元年 1970-01-01 00:00:00 到某个时间之间的毫秒值

// 时间戳
    // Instant:以 Unix 元年 1970-01-01 00:00:00 到某个时间之间的毫秒值
    @Test
    public void test2() {
        // 默认获取 UTC 时区 (UTC:世界协调时间)0时区时间
        Instant ins1 = Instant.now();
        System.out.println(ins1);
        // 带偏移量的时间日期 (如:UTC + 8)
        OffsetDateTime odt = ins1.atOffset(ZoneOffset.ofHours(8));
        System.out.println(odt);
        
         //转换成对应的毫秒值
        long milli = ins1.toEpochMilli();
        System.out.println(milli);
         //构建时间戳
        Instant ins2 = Instant.ofEpochSecond(6000);
        System.out.println(ins2);//1970-01-01T01:40:00Z
        
    }

4.时间/日期之间的间隔

• Duration:计算两个时间之间的间隔
• Period:计算两个日期之间的间隔
 //Duration:计算两个时间之间的间隔
   // Period:计算两个日期之间的间隔
    
     //Duration:计算两个时间之间的间隔
    @Test
    public void test3() {
        
        Instant ins1 = Instant.now();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Instant ins2 = Instant.now();
        Duration duration = Duration.between(ins1, ins2);
        System.out.println(duration.toMillis());
        
    }
    
    // Period:计算两个日期之间的间隔
    @Test
    public void test4() {
        
        LocalDate ld1 = LocalDate.of(2019, 11, 11);
        LocalDate ld2 = LocalDate.now();
        Period period = Period.between(ld1, ld2);
        System.out.println(period.getDays());
        
        
    }

5.时间校正器

image.png

// TemporalAdjusters 时间校正器
    @Test
    public void test1() {
        
        LocalDateTime ldt = LocalDateTime.now();
        System.out.println(ldt);
        
         //指定日期时间中的 年 月 日 ...
        LocalDateTime ldt2 = ldt.withDayOfMonth(10);//将月中的时间制定成10
        System.out.println(ldt2);
        
         //指定时间校正器
        LocalDateTime ldt3 = ldt.with(TemporalAdjusters.next(DayOfWeek.FRIDAY));
        System.out.println(ldt3);
        
        //自定义时间校正器:下一个工作日是什么时候
        
        LocalDateTime ldt4=ldt.with((l)->{
            LocalDateTime ldt0 =(LocalDateTime) l; 
            DayOfWeek dow = ldt0.getDayOfWeek();
             if (dow.equals(DayOfWeek.FRIDAY)) {
                    return ldt0.plusDays(3);
                } else if (dow.equals(DayOfWeek.SATURDAY)) {
                    return ldt0.plusDays(2);
                } else {
                    return ldt0.plusDays(1);
                }
    
        });
        System.out.println(ldt4);
    }

6.时间格式化

 DateTimeFormatter:格式化时间 / 日期
//DateTimeFormatter:格式化时间 / 日期
    @Test
    public void test1() {
        DateTimeFormatter dtf = DateTimeFormatter.ISO_DATE_TIME;
        LocalDateTime ldt = LocalDateTime.now();
        System.out.println(dtf.format(ldt));
        
         //自定义格式化 ofPattern
        
        DateTimeFormatter dtf2 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String d2 = dtf2.format(ldt);
        System.out.println(d2);
        
        //解析
        LocalDateTime ldt2 = ldt.parse(d2,dtf2);
        System.out.println(ldt2);
    }

image.png
7.时区的处理

• ZonedDate
• ZonedTime
• ZonedDateTime
// 时区的处理
    // ZonedDate
    // ZonedTime
    // ZonedDateTime
    
    @Test
    public void test1() {
         //查看支持的时区
        Set<String> set = ZoneId.getAvailableZoneIds();
        set.forEach(System.out::println);
        
        //指定时区
        LocalDateTime ldt1 = LocalDateTime.now(ZoneId.of("Chile/Continental"));
        System.out.println(ldt1);
        
        //带时区
        ZonedDateTime zdt1  = ldt1.atZone(ZoneId.of("Chile/Continental"));
        System.out.println(zdt1);
    }

9.重复注解与注解类型

//容器类
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)//声明周期
public @interface MyAnnotations {
    MyAnnotation[] value();
}
//自定义注解类
@Repeatable(MyAnnotations.class)//制定容器
@Target({ TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE })
@Retention(RetentionPolicy.RUNTIME) // 声明周期
public @interface MyAnnotation {
    String value() default "Java 8";
}
     //重复注解
    @Test
    @MyAnnotation("Hello")
    @MyAnnotation("World")
    public void test01() throws NoSuchMethodException {
        Class<TestAnnotation> clazz = TestAnnotation.class;
        Method test01 = clazz.getMethod("test01");
        MyAnnotation[] mas = test01.getAnnotationsByType(MyAnnotation.class);
        for (MyAnnotation ma : mas) {
            System.out.println(ma.value());
        }
    }

Java 8 新增注解:新增ElementType.TYPE_USE
和ElementType.TYPE_PARAMETER(在Target上)