终极CURD-4-java8新特性

目录1概述2lambda表达式2.1lambda重要知识点总结2.2java内置函数接口2.3方法引用2.4构造器引用2.5数组引用2.6lambda表达式的陷阱3Stream3.1stream三个核心步骤3.2stream创建的5个方法3.3stream中间操作3.4stream终止操作3.5并行流和串行流3.6fork/join4java8中默认的接口和方法5Optional5.1Option...

终极CURD-4-java8新特性

目录

  • 1 概述
  • 2 lambda表达式
    • 2.1 lambda重要知识点总结
    • 2.2 java内置函数接口
    • 2.3 方法引用
    • 2.4 构造器引用
    • 2.5 数组引用
    • 2.6 lambda表达式的陷阱
  • 3 Stream
    • 3.1 stream 三个核心步骤
    • 3.2 stream 创建的5个方法
    • 3.3 stream 中间操作
    • 3.4 stream 终止操作
    • 3.5 并行流和串行流
    • 3.6 fork/join
  • 4 java8中默认的接口和方法
  • 5 Optional
    • 5.1 Optional 精华所在
    • 5.2 Optional 误区
  • 6 java8 新的时间api
  • 7 重复注解和类型注解

1 概述

本篇博客主要介绍java8新特性,因为自己平常也使用到了一些java8的新特性,但是从来没有从头到尾,真真正正的把几乎所有常用的java8新特性研究一遍,这次借助端午节3三天,好好把常用java8新特性梳理一下,毕竟java12马上就要出来了。

本篇博客不会重点介绍java8新特性的基础内容,因为网上有很多优秀的文章已经介绍,比如像lambda表达式的用法,stream的特性,Optional的基础使用等等。在下就不重复造轮子了,我会给出几个我认为非常优秀的文章,对java8还不熟悉的读者,可以先看这些基础内容。本篇博客主要聚焦于java8新特性难点和易错点。

2 lambda表达式

如果对lambda还不清楚的读者,可以先按顺序阅读下面两篇文章

​ https://www.runoob.com/java/java8-lambda-expressions.html

​ http://blog.oneapm.com/apm-tech/226.html

2.1 lambda重要知识点总结

①lambda仅仅是一个语法糖,编译成class文件后,就可以发现,lambda变成了内部类

②一个lambda表达式,看成是一个匿名对象

③lambda表达式 所代表的的接口必须是一个函数式接口

④ 函数式接口:接口中只有一个抽象方法,就称为函数式接口。可以使用@FunctionalInterface确保该接口为函数式接口,如果不小心写错,java编译器会报错。

注意:函数式接口可以包含多个default方法和Object方法,以下程序编译器不会报错

@FunctionalInterfacepublic interface MyFunction { void hello(); @Override boolean equals(Object o); default String sayHi() {  System.out.println(“hi ... everyone i am super ...“);  return “hi“; }}

⑤函数式接口的用处,某些方法的参数,可以使用这些函数式接口,那么我们在调用这些方法的时候,就可以使用lambda表达式。函数式接口的唯一作用,就是为了lambda的使用

lambda重点在于参数和body,所以参数类型和body逻辑一定要正确,java编译期会自动将lambda转换成对象。

 @Test // 使用匿名对象 public void test1(){  Comparable<Integer>  comparable = new Comparable<Integer>() {@Overridepublic int compareTo(Integer o) { return 1;}  }; } @Test  // 使用lambda表达式 public void test2(){  Comparable<Integer> comparable = (o) -> 1; }  /* 想一想 为什么 下面4个会报错  (o) -> “1“  (o) -> System.out.print(“abc“)  () -> 1;  (String o) -> 1; 原因:java编译器现在已经非常强大,可以根据上下文推到lambda表达式参数和body,因为lambda表达式 实际上就是一个匿名对象,因此lambda的参数类型和body代码逻辑必须要符合匿名对象的格式  */

⑦lambda 表达式的局部变量可以不用声明为 final,但是必须不可被后面的代码修改(即隐性的具有 final 的语义,编辑器会自动帮我们加上final)

2.2 java内置函数接口

java内置函数接口,不需要我们自己写函数接口。最常用的四大函数接口是,Consumer、Supplier、Function、Predicate所有内置函数接口均在 java.util.function

为什么java会内置如此多的函数式接口,我们明明可以自己写函数式接口啊?

​ 原因:因为函数式接口非常简单,一个接口声明,加上一个抽象方法。既然如此,java的内置函数式接口就是业界规定,是一种规范。比如我想写一个Myfunction函数式接口

public interface MyFunction<T> {  void consume(T t);}

而别人又写宁一个MyFunction2、MyFunction3,既然如此,那大家就都用java内置的函数式接口,约定很重要!!!

@FunctionalInterfacepublic interface Consumer<T> { void accept(T t);}

2.3 方法引用

核心:方法引用只是lambda表现的另一种形式,它依旧还是一个匿名内部对象

1 对象::实例方法 该方法的参数类型和返回类型和函数式接口中的抽象方法必须一致

  // 1 匿名对象  Consumer<String> consumer1 = new Consumer<String>() {@Overridepublic void accept(String s) { System.out.println(s);}  };  // 2 lambda表达式  Consumer<String> consumer2 = s -> System.out.println(s);  // 3 方法引用 1  PrintStream printStream = System.out;  Consumer<String> consumer3 = printStream::println;  // 4 方法引用 2  Consumer<String> consumer4 = System.out::println;

2 类::静态方法 该方法的参数类型和返回类型和函数式接口中的抽象方法必须一致

 @Test public void test4(){  // 1  Comparator<Integer> comparator1 = (x,y) -> Integer.compare(x,y);  // 2  Comparator<Integer> comparator2 = Integer::compare; }

3 类::实例方法 特殊 该方法的参数类型和返回类型和函数式接口中的抽象方法肯定不一致,不一致,不一致

 @Test public void test5(){  // 1  BiPredicate<String, String> bp = (x, y) -> x.equals(y);  // 2  BiPredicate<String, String> bp2 = String::equals; } /* 若Lambda 的参数列表的第一个参数,是实例方法的调用者,第二个参数(或无参)是实例方法的参数时,格  式: ClassName::MethodName  比如 x就是body中的调用者,而y正是该方法的参数 */

比如下,下面的 类::静态方法 类::实例方法 是等价的

public class TestUtil { // 实例方法  没有参数 private TestUtil println2() {  return new TestUtil(); }  // 静态方法 有参数 private static TestUtil println3(TestUtil testUtil) {  return testUtil; } public static void main(String[] args) {  TestUtil testUtil = new TestUtil();  // 实例方法引用 函数式接口和实际方法参数肯定不一致  Function<TestUtil, TestUtil> function2222 = TestUtil::println2;  // 静态方法引用 函数式接口和实际方法参数必须一致  Function<TestUtil, TestUtil> function33333 = TestUtil::println3; }}

实际上TestUtil::println2,TestUtil::println3都可以看成是一个匿名对象,java编译过后,class文件几乎是一样的。

平常我们还是写 () -> {} 这种形式的,如果存在方法引用或者构造器引用,idea会自动提示我们,我们再修改即可。但是最好还是要给出详细注释,因为有的方法引用,一时半会看不明白。

2.4 构造器引用

构造器参数类型必须和函数式接口中的抽象方法一致,返回类型默认就是该类

 @Test public void test6(){  // 1  Supplier<Employee> supplier1 = () -> new Employee();  // 2 默认使用Employee无参构造器,因为Supplier的get方法没有任何参数  Supplier<Employee> supplier2 = Employee::new;  // 3 Employee必须要有一个 Integer构造器public Employee(Integer age)  Function<Integer,Employee> function = Employee::new; }

2.5 数组引用

构造器参数类型必须和函数式接口中的抽象方法一致,返回类型默认就是该数组

 @Test public void test7(){  // 1  Function<Integer,String[]> function1 = x -> new String[x];  // 2  Function<Integer,String[]> function2 = String[]::new; }

2.6 lambda表达式的陷阱

有一篇博客非常棒,推荐给大家

https://segmentfault.com/a/1190000018857239

3 Stream

集合讲的是数据,流讲的是计算!

注意:

①Stream 自己不会存储元素。

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

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

3.1 stream 三个核心步骤

一:创建 Stream

二:中间操作

三:终止操作(终端操作)

3.2 stream 创建的5个方法

  //1. Collection 提供了两个方法  stream() 与 parallelStream()  List<String> list = new ArrayList<>();  Stream<String> stream = list.stream(); //获取一个顺序流  Stream<String> parallelStream = list.parallelStream(); //获取一个并行流    //2. 通过 Arrays 中的 stream() 获取一个数组流  Integer[] nums = new Integer[10];  Stream<Integer> stream1 = Arrays.stream(nums);    //3. 通过 Stream 类中静态方法 of()  Stream<Integer> stream2 = Stream.of(1,2,3,4,5,6);    //4. 创建无限流  //迭代  Stream<Integer> stream3 = Stream.iterate(0, (x) ->
源文地址:https://www.guoxiongfei.cn/cntech/19308.html
0