JDK8 新特性【部分】
本章目标
- 
重点掌握 Lambda 表达式的使用 
- 
掌握新日期/时间 API 的使用 
- 
掌握 Optional 类的使用 
- 
掌握接口增强的使用 
Lambda 表达式
Lambda 表达式的出现
针对使用匿名内部类语法冗余的问题,JDK8 推出了 Lambda 表达式。
- 
Lambda 表达式体现的是函数式编程思想,只需要将要执行的代码放到函数中即可(函数就是类中的方法) 
- 
Lambda 表达式就是一个匿名函数,我们只需要将执行的代码放到 Lambda 表达式中即可 
Lambda 表达式语法格式
Lambda 表达式省去面向对象的条条框框,Lambda 的标准格式由 3 部分组成:
(参数类型 参数名称) -> {
    方法体;
    return 返回值;
}
- 
(参数类型 参数名称):参数列表部分 
- 
{...}:方法体,即要执行的代码部分 
- 
->:箭头,无实际含义,起到连接参数列表和方法体的作用 
Lambda 表达式的省略规则
- 
小括号中的参数类型可以省略 
- 
如果小括号中只有一个参数,那么可以省略小括号 
- 
如果大括号中只有一条语句,那么可以同时省略大括号、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

创建 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("打球"); } }

 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号