java8新特性

一、概述

1.Lambda表达式 lambda 表达式只能引用标记了 final 的外层局部变量,这就是说不能在 lambda 内部修改定义在域外的局部变量,否则会编译错误。
2.函数式接口
3.方法引用与构造器引用
4.Stream API
5.接口中的默认方法与静态方法
6.新时间日期API
7.其他新特性

二、优点

1.速度更快(HashMap底层 数组 + 链表)(去掉了永久区,新增元空间 MetaSpace使用物理内存)
2.代码更少(增加了新的语法Lambda表达式)
3.强大的Stream API
4.便于并行
5.最大化减少空指针异常 Optional

三、Lambda表达式

1.为什么使用Lambda表达式

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

2.Lambda表达式基础语法

java8中引入了一个新的操作符 -> 该操作符称为 箭头操作符 或者 lambda操作符
箭头操作符 将Lanbda表达式拆成两部分 
左侧:Lambda表达式的参数列表
右侧:Lambda表达式所需要执行的功能

语法格式一:无参无返回值

() -> System.out.println("Hellow World!")
Runnable r = () -> System.out.println("Hellow Lambda")
r.run();

语法格式二:有一个参数无返回值

Consumer<String> s = (x) -> System.out.println(x)
s.accept("我爱你中国!");

语法格式三:若只有一个参数,小括号可以不写

Consumer<String> s = x -> System.out.println(x)
s.accept("我爱你中国!");

语法格式四:有多个参数,且Lambda体中有多条语句(必须用{}),有返回值

Comparator<Integer> com = (x,y) -> {
        System.out.println(x);
        return Integer.compare(x,y);
}
com.compare(1,2);

语法格式五:Lambda体中只有一条语句{}和return可以都不写

Comparator<Integer> com = (x,y) -> Integer.compare(x,y);
com.compare(1,2);

语法格式六:Lambda表达式的参数列表中的数据类型可以不写,因为JVM编译器可以通过上下文推断出数据类型,即"类型推断"

Comparator<Integer> com = (Integer x, Integer y) -> Integer.compare(x,y);
com.compare(1,2);

3.Lambda表达式需要函数式接口的支持

函数式接口:接口中只有一个抽象方法的接口,称之为函数式接口
可以使用注解@FunctionalInterface 修饰,可以检查该接口是否是函数式接口。

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

1. Consumer<T>: 消费型接口
    void accept(T t);
2.Supplier<T>: 供给型接口
    T get();
3.Function<T, R>: 函数型接口
    R apply(T t);
4.Predicate<T>: 断言型接口
    boolean test(T t);

5.方法引用

若Lambda体中的内容有方法已经实现了,我们可以使用"方法引用"(可以理解为方法引用是Lambda表达式的另外一种表现形式)
主要有三种语法格式:
1.对象::实例方法名

Supplier<String> sup = () -> System.out.println("Hellow world!");
// 对象::实例方法名
PrintStream ps = System.out;
Supplier<String> sup = () -> ps.println("Hellow world!");

Supplier<String> sup = ps::println("Hellow world!");

2.类::静态方法名

Comparator<Integer> com = (x,y) -> Integer.compare(x,y);
// 类::静态方法名
Comparator<Integer> com =  Integer::compare;

3.类::实例方法名(非静态方法名)

BiPridicate<String,String> bp2 = (x,y) -> x.equals(y);

BiPridicate<String,String> bp2 = String::equals;

使用注意事项:
a.Lambda体中的参数列表和返回值类型,要与函数式接口中抽象方法的参数列表和返回值类型一致。
b.若Lambda的参数列表中的第一个参数是实例方法的调用方,第二个参数是实例方法的参数时可以使用格式3 (类::实例方法名)
总结:在JDK8之前,可以使用匿名内部类,调用接口方法,JDK8以及之后,可以使用lambda表达式,以及可以使用方法引用。

// JDK8之前
Employee emp = new Emloyee();
Supplier<String> su = new Supplier(){
    @override
    public String get() {
        return emp.getName();
    };
}
String name = su.get();
System.out.println(name);
// JDK8 lambda表达式
Employee emp = new Emloyee();
Supplier<Integer> su = () -> emp.getName();
String name = su.get();
System.out.println(name);
// 使用方法引用
Employee emp = new Emloyee();
Supplier<Integer> su = () -> emp::getName;
String name = su.get();
System.out.println(name);

6.构造器引用

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


Supplier<Employee> sup = () -> new Employee();

Supplier<Employee> sup2 = Employee::new;

Employee employee = sup2.get();

System.out.println(employee);

7.数组引用

格式: Type::new


Function<Integer,String[]> fun = (x) -> new String[x];

String[] str = fun.apply(10);

System.out.println(str.length);
// 数组引用
Function<Integer,String[]> fun = String[]::new;
String[] str1 = fun.apply(20);
System.out.println(str1.length);

四、Stream 流

Stream 三步走:
1.创建流

创建流的方式:
a.通过Collection 提供的stream()【串行流】以及parallelStream()【并行流】方法获取
    List<String> list = new ArrayList<>();
    Stream<String> stream1 = list.stream();
b.通过Arrays中的静态方法stream()
    Employee[] e = new Employee[10]; 
    Stream<Employee> stream2 = Arrays.stream(e);
c.通过Stream中的静态方法of()
    Stream<String> stream3 = Stream.of("a","b","c");
d.创建无限流
    // 迭代
    Stream<Integer> stream4 = Stream.iterate(0,(x) -> x+2 );
    // 生成
    Stream<Double> stream5 = Stream.generate(() -> Math.random());

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

第一:筛选 / 切片
    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 test01(){
    emps.stream()
        .filter((x) -> x.getAge() > 35)
        .limit(3) //短路?达到满足不再内部迭代
        .distinct()
        .skip(1)
        .forEach(System.out::println);

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

@Test
public void test02(){
    List<String> list = Arrays.asList("a", "b", "c");
    list.stream()
        .map((str) -> str.toUpperCase())
        .forEach(System.out::println);
}

public Stream<Character> filterCharacter(String str){
    List<Character> list = new ArrayList<>();
    for (char c : str.toCharArray()) {
        list.add(c);
    }

    return list.stream();
}

@Test
public void test03(){
    List<String> list = Arrays.asList("a", "b", "c");
    Test02 test02 = new Test02();
    list.stream()
        .flatMap(test02::filterCharacter)
        .forEach(System.out::println);
}
第三:排序
    sorted():自然排序Comparable
    sorted(Comparator c):定制排序

@Test
public void test04(){
    List<Integer> list = Arrays.asList(1,2,3,4,5);
    list.stream()
        .sorted() //comparaTo()
        .forEach(System.out::println);
}

@Test
public void test05(){
    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);
}

3.终止操作

allMatch:检查是否匹配所有元素
anyMatch:检查是否至少匹配一个元素
noneMatch:检查是否没有匹配所有元素
findFirst:返回第一个元素
findAny:返回当前流中的任意元素
count:返回流中元素的总个数
max:返回流中最大值
min:返回流中最小值
public enum Status {
    FREE, BUSY, VOCATION;
}

@Test
public void test01(){
    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);

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

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

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

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

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

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


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

Collector接口中的方法的实现决定了如何对流执行收集操作(如收集到List、Set、Map)。但是Collectors实用类提供了很多静态方法,可以方便的创建常见收集器实例,具体方法与实力如下:

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(){
  //放入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 test03(){
  //总数
  Long count = emps.stream()
      .collect(Collectors.counting());
  System.out.println(count);

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

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

  //最大值
  Optional<Employee> max = emps.stream()
      .collect(Collectors.maxBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
  System.out.println(max.get());

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

@Test
public void test04(){
  //分组
  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);
}

@Test
public void test05(){
  //总结
  DoubleSummaryStatistics dss = emps.stream()
      .collect(Collectors.summarizingDouble(Employee::getSalary));
  System.out.println(dss.getMax());
  System.out.println(dss.getMin());
  System.out.println(dss.getSum());
  System.out.println(dss.getCount());
  System.out.println(dss.getAverage());
  
  //连接
  String str = emps.stream()
      .map(Employee::getName)
      .collect(Collectors.joining("-")); //可传入分隔符
  System.out.println(str);
}
按照什么样的方式进行结果的收集 List Set Map
Collectors类提供了很多的静态方法,用于创建常见收集器实例
Collectors.toList() Collectors.toSet() Collectors.toCollection(HashSet::new) Collectors.toCollection(LinkedList::new)
统计总数 Collectors.counting()
平均值 Collectors.averagingDouble()
总和 Collectors.summingDouble()
最大值 Collectors.maxBy()
最小值 Collectors.minBy()
分组  Collectors.groupingBy() 得到的结果是一个Map<k,v>  v -list
多级分组
分区  两个区  true  false

五、接口中的静态方法与默认方法

     接口默认方法的"类优先"原则
     若一个接口定义了一个默认方法,而另外一个父类或接口中又定义了一个同名的方法时
     1.选择父类中的方法。如果一个父类提供了具体的实现,那么接口中具有相同名称和参数的默认方法会被忽略。
     2.接口冲突。如果一个父接口提供一个默认方法,而另一个接口也提供了一个具有相同名称和参数列表的方法(不管方法
     是否哦时默认方法),那么必须覆盖方法来解决冲突。

六、新时间日期API

 以前的时间API 可能有线程安全问题,新版的解决了这个问题
 LocalDate、LocalTime、LocalDateTime 实例是不可变的对象
 Instant : 时间戳 (以unix元年,1970年1月1日 00:00:00 到某个时间之间的毫秒值)
 计算两个时间之间的间隔 Duration
 计算两个日期之间的间隔 Period
 时间校正器
    TemporalAdjuster: 有时我们需要获取例如:将日期调整到‘下个周日’等操作
    TemporalAdjusters :该类提供了大量的静态方法操作TemporalAdjuster
  时间日期格式化 DateTimeFormatter

带时区的时间处理
         ZonedDate ZonedTime ZonedDateTime
         其中每个时区都对应着id,地区id都为   {区域}/{城市} 的格式 例如 Asia/Shanghai
         ZoneId:该类包含了所有的时区信息

summer
30 声望3 粉丝

这一路我披荆斩棘,只为遇见你


« 上一篇
跨域问题