JDK8新特性详解Lambda、StreamAPI、Optional等(一)

简介: JDK8新特性详解Lambda、StreamAPI、Optional等(一)

JDK8学习笔记


一、JDK8新特性

1. Lambda表达式
2. 接口的增强
3. 函数式接口
4. 方法引用
5. Stream API
6. Optional
7. 新时间日期API

二、Lambda表达式

1. 需求分析

创建一个新的线程,指定线程要执行的任务

public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("当前线程名:"+Thread.currentThread().getName());
            }
        }).start();
        System.out.println("主线程名字:"+ Thread.currentThread().getName());
    }

代码分析:

  1. Thread类需要一个Runnable接口作为参数,其中的抽象方法run是用来指定线程任务内容的核心
  2. 为了指定run方法体,不得不需要Runnable的的实现类
  3. 为了省区定义一个Runnable的实现类,不得不使用匿名内部类
  4. 必须覆盖重写抽象的run方法,所有的方法名称,方法参数,方法返回值都不得不重写一遍,而且不能出错。
  5. 而实际上,我们只在乎方法体中的代码

2. Lambda表达式初体验

Lambda表达式是一个匿名函数,可以理解为一段可以传递的代码

new Thread(()->{System.out.println("Lambda线程名字:"+ Thread.currentThread().getName());}).start();

Lambda表达式的有点:简化了匿名内部类的使用,语法更加简单。

匿名内部类语法冗余,体验了Lambda表达式和,发现Lambda表达式是简化匿名内部类的一种方式。

3. Lambda表达式语法规则

lambda省去了面向对象的条条框框,Lambda的标注格式由3部分组成

(String[] args) ->{
  代码体   
}

格式说明:

  • (参数类型、参数名称):参数列表
  • (代码体):方法体
  • ->:分割参数列表和方法体

3.1 无参无返回值的Lambda

定义一个接口

public interface UserService {
    public void show();
}

然后创建主方法使用

public static void main(String[] args) {
    goShow(new UserService() {
        @Override
        public void show() {
            System.out.println("show方法执行了:"+ Thread.currentThread().getName());
        }
    });
    goShow(() ->{
        System.out.println("Lambda的show方法执行了:"+Thread.currentThread().getName());
    });
}
public static void goShow(UserService userService){
    userService.show();
}

输出:

方法名为:main
Lambda表达式方法名字:main

3.2 有参有返回值的Lambda

创建Person对象

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Person {
    private String name;
    private Integer age;
    private Integer height;
}

在List集合中保存多个Person对象,然后根据这些对象做age排序操作

public static void main(String[] args) {
     List<Person> list = Arrays.asList(
         new Person("周杰伦",27,175),
         new Person("周星驰",32,157),
         new Person("周公瑾",182,188),
         new Person("周恩来",82,177)
     );
     Collections.sort(list, new Comparator<Person>() {
         @Override
         public int compare(Person o1, Person o2) {
             return o1.getAge() - o2.getAge();
         }
     });
     for (Person p: list){
         System.out.println( p .toString());
     }
     System.out.println("----------------------------");
     Collections.sort(list,(Person o1,Person o2)->{
         return o2.getAge() - o1.getAge();
     });
     for (Person p: list){
         System.out.println("lambda====" + p .toString());
     }
 }

我们发现sort方法的第二个参数是一个Comparator接口的匿名内部类,且执行的方法有参数和返回值的表达式

输出结果

Person(name=周杰伦, age=27, height=175)
Person(name=周星驰, age=32, height=157)
Person(name=周恩来, age=82, height=177)
Person(name=周公瑾, age=182, height=188)
----------------------------
lambda====Person(name=周公瑾, age=182, height=188)
lambda====Person(name=周恩来, age=82, height=177)
lambda====Person(name=周星驰, age=32, height=157)
lambda====Person(name=周杰伦, age=27, height=175)

4. @FunctionalInterface注解

/**
 * @FunctionalInterface
 * 这是一个标志注解,被该注解修饰的接口只能声明一个抽象方法
 */
@FunctionalInterface
public interface UserService {
    public void show();
}

5. Lambda表达式的原理

匿名内部类会在编译的时候产生一个class文件

Lambda表达式在程序运行的 时候会形成一个类

  1. 在类中新增了一个方法,这个方法的方法体就是Lambda表达式中的代码
  2. 还会形成一个匿名内部类,实现接口,重写抽象方法
  3. 在接口中重写方法会调用新生成的方法

6. Lambda表达式省略写法

在lambda表达式的标准写法基础上,可以使用省略写法的规则为:

  1. 小括号内的参数类型可以省略
  2. 如果小括号内有且仅有一个参数,则小括号可以省略
  3. 如果大括号内有且仅有一个语句,则可以同时省略大括号,return关键字以及分号。
public static void main(String[] args) {
    goOrderShow((String name) ->{
        System.out.println(name);
        return name+"666";
    });
    goStudyShow((String name ,Integer age) ->{
        System.out.println(name + age);
        return  name + age +"777";
    });
    System.out.println("Lambda简化写法");
    goOrderShow(name -> name+"6666");
    goStudyShow((name ,age) -> name + age + "7777");
}
public static void goOrderShow(OrderService orderService){
    orderService.show("张三");
}
public static void goStudyShow(StudentService studentService){
    studentService.show("李四",32);
}

7. Lambda表达式的使用前提

Lambda表达式的语法是非常简洁的,但是Lambda表达式不是随便使用的,必须满足两个条件

  1. 方法的参数或者局部变量类型必须为接口才能使用Lambda
  2. 接口中有且仅有一个抽象方法(@FunctionalInterface)

8. Lambda和匿名内部类的对比

  1. 所需类型不一样
  • 匿名内部类的类型可以是类、抽象类、接口
  • Lambda表达式需要的类型必须是接口
  1. 抽象方法的数量不一样
  • 匿名内部类所需的接口中的抽象方法的数量是随意的
  • Lambda表达式所需的接口中只能有一个抽象方法
  1. 实现原理不一样
  • 匿名内部类再编译后形成一个class
  • Lambda表达式是在程序运行的时候动态生成class

1. 新增方法

在JDK8针对接口做了增强,在JDK8之前

interface 接口名{
    静态常量;
    抽象方法;
}

JDK8之后

interface 接口名{
    静态常量;
    抽象方法;
    静态方法;
    默认方法;
}

2. 默认方法

2.1为什么增加默认方法

在JDK8以前接口中只有抽象方法和静态常量,会存在一下问题

如果接口中有新增抽象方法,那么实现类必须抽象这个抽象方法,非常不利与接口扩展

2.2 接口默认方法的格式

接口默认方法的语法格式是

interface 接口名{
    修饰符 default 返回值类型 方法名{
        方法体;
    }
}

2.3 接口中默认方法的使用

接口中的默认方法有两种使用方式

  1. 实现类直接调用接口的默认方法
  2. 实现类重写接口的方法

3. 静态方法

JDK8中为接口新增了静态方法,作用也是为了接口的扩展

3.1 语法规则

interface 接口名{
    修饰符 static 返回值类型 方法名字{
        方法体;
    }
}

3.2 静态方法的使用

public class Demo01Interface {
    public static void main(String[] args) {
        B b = new B();
        System.out.println(b.test1());
        System.out.println(b.test2());
        System.out.println(A.test3());
    }
}
interface A{
    String test1();
    public default String test2(){
        return "接口新增了默认方法,可以被实现类重写,必须实例化调用";
    }
    public static String test3(){
        return "接口新增了静态方法,不能被实现类重写,类名.方法名调用";
    }
}
class B implements A{
    @Override
    public String test1() {
        return "接口抽象方法";
    }
}

接口中的静态方法在实现类中是不能被重写的。调用只能通过接口类型来实现:接口名.静态方法();

4. 两者的区别

  1. 默认方法通过实例调用,静态方法通过接口名调用
  2. 默认方法可以被继承,实现类可以直接调用接口默认方法,也可以重写接口默认方法
  3. 静态方法不能被继承,实现类不能重写接口的静态方法,只能通过接口名调用

四、 函数式接口

1. 函数式接口的由来

我们知道使用Lambda表达式的前提是需要有函数式接口,而Lambda表达式使用时不关心接口名字、抽象方法名。只关心抽象方法的参数列表和返回值类型。因此为了让我们使用Lambda表达式更加的方便,JDK中提供了大量常用的函数式接口。

2. 函数式接口介绍

在JDK中帮我们提供的有函数式接口,主要是在Java.util.function包中

2.1 Supplier

无参有返回值的接口,对于Lambda表达式需要提供一个返回数据的类型。

@FunctionalInterface
public interface Supplier<T> {
    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}

使用

public static void main(String[] args) {
        fun1(()->{
            int[] arr = {7,3,5,12,42,1};
//            int max = 0;
//            for (int i : arr){
//                if(i>max){
//                    max = i;
//                }
//            }
//            return max ;
            Arrays.sort(arr);
            return arr[arr.length -1];
        });
    }
    public static void fun1 (Supplier<Integer> supplier){
        //get方法是一个无参有返回值的抽象方法
        Integer max = supplier.get();
        System.out.println("Max ====="+ max);
    }

2.2 Consumer

有参数无返回值的接口,前面介绍的Supplier是接口用来生产数据的,而Consumer是用来消费数据的。使用的时候需要指定一个泛型来定义参数类型

@FunctionalInterface
public interface Consumer<T> {
    void accept(T var1);
}

使用

public class ConsumerTest {
    public static void main(String[] args) {
        fun1(a-> a+=12);
    }
    public static void fun1 (Consumer<Integer> consumer){
        int a = 32;
        System.out.println(a);
        consumer.accept(a);
        System.out.println(a);
    }
}

默认方法:andThen

如果一个方法的参数和返回值全部是Consumer类型,那么就可以实现效果,消费一个数据的时候,首先做一个操作,然后再做一个操作,实现组合,而这个方法就是Consumer接口中的default方法andThen方法

default Consumer<T> andThen(Consumer<? super T> var1) {
     Objects.requireNonNull(var1);
     return (var2) -> {
         this.accept(var2);
         var1.accept(var2);
     };
 }

具体操作

public class ConsumerAndThenTest {
    public static void main(String[] args) {
        func(msg ->{
            System.out.println("转换为小写》》》》》"+msg.toLowerCase(Locale.ROOT));
        },msg2 ->{
            System.out.println("转换为大写》》》》》"+msg2.toUpperCase(Locale.ROOT));
        });
    }
    public static void func (Consumer<String> c1,Consumer<String> c2){
//        c1.accept("ZhangSan");
//        c2.accept("ZhangSan");
//        c1.andThen(c2).accept("ZhangSan");
        c2.andThen(c1).accept("ZhangSan");
    }
}

2.3 Function

有参有返回值的接口,Function接口是根据一个类型的数据得到另一个类型的数据,前者称为前置i套件,后者成为后置条件,有参数有返回值

@FunctionalInterface
public interface Function<T, R> {
    /**
     * Applies this function to the given argument.
     *
     * @param t the function argument
     * @return the function result
     */
    R apply(T t);
}

使用:传入一个字符串返回一个数字

public class FunctionTest {
    public static void main(String[] args) {
        int a = func(msg -> Integer.parseInt(msg));
        System.out.println(a);
    }
    public static Integer func(Function<String,Integer> fun){
        return fun.apply("322");
    }
}

默认方法:andThen,也是进行组合操作

public class FunctionAndThenTest {
    public static void main(String[] args) {
        Integer result = func(msg -> Integer.parseInt(msg),msg2-> msg2*10);
        System.out.println(result);
    }
    public static Integer func (Function<String,Integer> f1,Function<Integer,Integer> f2){
//        int a = f1.apply("32");
//        int b = f2.apply(a);
//        return b;
        return f1.andThen(f2).apply("54");
    }
}

默认的compose方法的作用顺序和andThen刚好相反

而静态方法identity则是,输入什么参数就返回什么参数

目录
相关文章
|
5天前
|
安全 JavaScript 前端开发
JDK1.8的重要的新特性与功能
Java Development Kit (JDK) 1.8,也称为Java 8,是Java平台的一个重大更新,于2014年3月发布。它引入了多项新特性、新的API和性能提升
102 3
|
5天前
|
Java
【JAVA进阶篇教学】第二篇:JDK8中Lambda表达式
【JAVA进阶篇教学】第二篇:JDK8中Lambda表达式
|
5天前
|
算法 Java 编译器
升级指南之JDK 11+ 新特性和AJDK
本文详细介绍了JDK个版本之间的特性、区别以及JDK版本更迭时优化了哪些地方,对JDK的版本选择给出了建议,以及升级教程。
|
5天前
|
存储 安全 Java
JDK22发布了!来看看有哪些新特性
以上是介绍 JDK22新特性的全部内容了,突然V哥想要感慨一下,技术之路,学无止境,选择 IT 技术,作个纯粹的人,享受研究技术的过程,这种带来的快感,也许只有真正热爱编程的人才能有体会。
|
5天前
|
Java
Java jdk1.8 lambda 遍历集合的时候到底需不需判空
Java jdk1.8 lambda 遍历集合的时候到底需不需判空
|
5天前
|
编解码 Java API
集合在JDK9中的新特性
集合在JDK9中的新特性
|
5天前
|
Java
JDK8新特性--lambda表达式
面向对象思想强调:必须通过对象的形式来做事情
JDK8新特性--lambda表达式
|
5天前
|
SQL Java API
浅析jdk8所包含的主要特性
浅析jdk8所包含的主要特性
|
5天前
|
IDE Java Shell
02|手把手教你安装JDK与配置主流IDE
02|手把手教你安装JDK与配置主流IDE
27 0
|
5天前
|
Java Shell 开发者
都2024年了!你还不知道在Docker中安装jdk?
都2024年了!你还不知道在Docker中安装jdk?