JDK8 新特性【部分】

JDK8 新特性

本章目标

  • 重点掌握 Lambda 表达式的使用

  • 掌握新日期/时间 API 的使用

  • 掌握 Optional 类的使用

  • 掌握接口增强的使用


Lambda 表达式

Lambda 表达式的出现

针对使用匿名内部类语法冗余的问题,JDK8 推出了 Lambda 表达式。

  • Lambda 表达式体现的是函数式编程思想,只需要将要执行的代码放到函数中即可(函数就是类中的方法)

  • Lambda 表达式就是一个匿名函数,我们只需要将执行的代码放到 Lambda 表达式中即可

Lambda 表达式语法格式

Lambda 表达式省去面向对象的条条框框,Lambda 的标准格式由 3 部分组成:

(参数类型 参数名称) -> {
   方法体;
   return 返回值;
}
  • (参数类型 参数名称):参数列表部分

  • {...}:方法体,即要执行的代码部分

  • ->:箭头,无实际含义,起到连接参数列表方法体的作用

Lambda 表达式的省略规则
  1. 小括号中的参数类型可以省略

  2. 如果小括号中只有一个参数,那么可以省略小括号

  3. 如果大括号中只有一条语句,那么可以同时省略大括号、return 关键字及语句分号

Lambda 表达式的使用
public class LambdaDemo {
    public static void main(String[] args) {
        //匿名内部类方式
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("新线程执行代码了");
            }
        }).start();
 
        //体验Lambda表达式
        new Thread(() ->{
            System.out.println("Lambda表达式执行了");
        }).start();
    }
}
Lambda 表达式的好处
  • 可以简化匿名内部类,让代码更加精简

  • Lambda 表达式免去了使用匿名方法的麻烦,并且给予Java简单但是强大的函数化的编程能力

Lambda 表达式的常见使用场景
  • 使用 Lambda 表达式替换线程 Runnable 匿名内部类

  • 使用 Lambda 表达式实现 Comparator 比较器

  • 使用 Lambda 表达式实现 ActionListener 按钮事件监听器

Lambda 表达式的使用限制条件
  • 方法的参数或局部变量类型必须为接口

  • 接口中有且仅有一个抽象方法

    @FunctionalInterface:JDK新增注解,用于检测接口只有一个抽象方法,否则会报错

Lambda 和 匿名内部类对比
所需的类型不一样
  • 匿名内部类:需要的类型可以使具体类、抽象类、接口

  • Lambda 表达式:需要的类型必须是接口

抽象方法数量不一样
  • 匿名内部类:接口中抽象方法的数量没有限制

  • Lambda 表达式:接口中的抽象方法的数量只能有 1 个

实现原理不同
  • 匿名内部类:是在编译后会形成额外的一个 类名$0 的.class文件

  • Lambda 表达式:是在程序运行的时候动态生成 .class 文件(Lambda 表达式最终的实现还是基于匿名内部类的方式)

总结

当接口中只有一个抽象方法时,建议使用 Lambda 表达式;其他情况下,还是需要使用匿名内部类

新日期/时间 API

老日期/时间的设计缺陷

在 JDK8 之前,我们经常使用到的时间 API 包括(Date、Calendar),Date 与字符串之间的转换使用 SimpleDateFormat 进行转换(parse()、format() 方法),然而 SimpleDateFormat 不是线程安全的。在设计上也是存在一些缺陷,如下:

  • 设计很差:在 java.util 和 java.sql 的包中都有日期类。java.util.Date 同时包含日期和时间,而java.sql.Date仅包含日期,此外用于格式化和解析的类又在 java.text 包中定义;

  • 非线程安全:java.util.Date 是非线程安全的,所有的日期类都是可变的,这是 java 日期类最大的问题之一;

  • 时区处理麻烦:日期类并不提供国际化,没有时区支持。因此 java 引入了 java.util.Calendar 和 java.util.TimeZone 类,但他们同样存在上述所有的问题

在 JDK8 中,引入了一套全新的时间日期 API,这套 API 在设计上比较合理,使用时间操作也变得更加方便。并且支持多线程安全操作。

新日期/时间 API 介绍

JDK8 中增加了一套全新的日期时间 API,这套 API 设计合理,是线程安全的。新的日期及时间 API 位于 java.time 包下,如下是一些该包下的关键类:

  • LocalDate:表示日期,包含:年月日。格式为:2020-01-13

  • LocalTime:表示时间,包含:时分秒。格式为:16:39:09.307

  • LocalDateTime:表示日期时间,包含:年月日 时分秒。格式为:2020-01-13T16:40:59.138

  • DateTimeFormatter:日期时间格式化类

  • Instant:时间戳类

  • Duration:用于计算 2 个时间(LocalTime,时分秒)之间的差距

  • Period:用于计算 2 个日期(LocalDate,年月日)之间的差距

  • ZonedDateTime:包含时区的时间

Optional 类

NullPointException 空指针异常问题

JDK8 以前,编写代码通常会出现 NullPointerException (空指针异常),通常情况下我们都是通过 if ... else...来对对象进行是否为空判断,然后再进行逻辑处理,代码写起来也比较冗余。

JDK8 新增了 Optional 类,使用该类可以避免我们对空指针的检查,使代码看起来比较优雅。

Optional 类介绍

Optional 类是一个没有子类的工具类,我们可以把 Optional 类看作是一个容器。这个容器它有两种情况:①要么有值 ②要么为null

image-20210908162344270

创建 Optional 类对象的 3 种方式
//1.创建一个 Optional 实例
Optional.of(T t);
//2.创建一个空的 Optional 实例
Optional.empty();
//3.若 t 不为 null,创建 Optional实例,否则创建空实例
Optional.ofNullable(T t);
public class OptionalDemo {
 
    public static void main(String[] args) {
        //1.1 通过Optional.of() 方法,只能传入一个具体指,不能传入null,传入null报空指针异常
        Optional<String> op1 = Optional.of("Lucy");
        //Optional<Object> op2 = Optional.of(null);
        System.out.println(op1);//Optional[Lucy]
        //System.out.println(op2);//java.lang.NullPointerException
 
        //1.2 通过Optional.ofNullable()方法(可以传入具体值,也可以传入null,并不会报空指针异常)
        Optional<String> op3 = Optional.ofNullable("Lucy");
        Optional<Object> op4 = Optional.ofNullable(null);
        System.out.println(op3);//Optional[Lucy]
        System.out.println(op4);//Optional.empty
 
        //1.3 通过 Optional.empty() 方法创建一个空 Optional,存入的是null
        Optional<Object> op5 = Optional.empty();
        System.out.println(op5);//Optional.empty
    }
}
Optional 类常用方法
  • isPresent() :判断是否包含值。包含值返回 true,不包含值返回 false

  • get(): 如果Optional有值则将其返回,否则抛出 NoSuchElementException: No value present 异常

  • orElse() : 如果调用对象包含值,返回该值,否则返回参数字符串str

  • orElseGet():如果调用对象包含值,返回该值。否则返回 s 获取的值

  • orElseThrows():如果存在该值,返回包含的值,否则抛出由 Supplier 继承的异常

  • map():如果有值对其处理,并返回处理后的Optional,否则返回 Optional.empty()

  • flatMap():如果有值,为其执行 mapping 函数返回 Optional 类型返回值,否则返回空Optional。

    flatMap() 与 map()方法类似,区别在于 mapping 函数的返回值不同。map() 方法的 mapping 函数返回值可以是任何类型T,在 map () 方法返回之前会包装为 Optional。而 flatMap() 方法的 mapping 函数必须是 Optional。调用结束时,flatMap不会对结果用Optional封装。

  • filter(): filter()方法通过传入限定条件对 Optional 实例的值进行过滤。如果有值并且满足 Predicate判断条件,则返回包含该值的Optional,否则返回空 Optional。

  • ifPresent():如果值存在则使用该值调用consumer,否则不做任何事情

  • ifPresentOrElse():JDK9以后提供。如果值存在则使用该值调用consume,否则执行自定义的 Runnalbe 操作

  • equals():判断其他对象是否等于Optional

接口增强

在JDK8之前,JDK规定接口中只能定义 ①静态常量 ②抽象方法

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

在JDK8之后,对接口进行了增强。我们可以在接口中定义 ①静态常量 ②抽象方法 ③默认方法 ④静态方法

修饰词 interface 接口名{
    静态常量;
    抽象方法;
    默认方法;
    静态方法;
}
接口增强特性
  • 新增的默认方法:使用 default 关键字,且实现类不必重写,可以直接使用(实现类也可以根据需要重写,这样就方便了接口的扩展)

  • 新增的静态方法:实现类既不能调用,也不能重写(只属于接口本身),只能通过接口名. (接口名+ .)的方式调用

接口增强举例:

public class InterfaceDemo {
    public static void main(String[] args) {
        IStudent.work();
        System.out.println(IStudent.ADDRESS);
        Student student = new Student();
        student.study();
        student.play();
    }
}
​
/**
 * 学生接口
 */
interface IStudent {
    //静态常量
    String ADDRESS="成都";
​
    //抽象方法(实现类必须重写)
    void play();
​
    //默认方法(实现类不必重写,可以直接使用,也可以根据需要重写)
    default void study() {
        System.out.println("学习大数据");
    }
​
    //静态方法(实现类既不能调用,也不能重写,只属于接口本身)
    static void work() {
        System.out.println("准备工作");
    }
}
​
/**
 * 学生实现类
 */
class Student implements IStudent {
    @Override
    public void play() {
        System.out.println("打球");
    }
}

 

posted @ 2021-09-13 19:06  嘛了对就念来过倒  阅读(77)  评论(0编辑  收藏  举报