文章目录
0. 前言
整个笔记参考视频:Java8新特性lambda&stream&optional实现原理(余胜军通俗易懂版本)
评价:视频讲的还算清楚,但不够细致,把几个主要的新特性的用法浅浅讲了一下。对新特性,尤其是 Optional
类个人评价是写起来很酷,维护起来想哭。
jdk8新特性
- Lambda 表达式:Lambda 允许把函数作为一个方法的参数(函数作为参数传递到方法中)。
- Stream API:新添加的Stream API(java.util.stream) 把真正的函数式编程风格引入到Java中。
- 方法引用:方法引用提供了非常有用的语法,可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,方法引用可以使语言的构造更紧凑简洁,减少冗余代码。
- Optional 类:Optional 类已经成为 Java 8 类库的一部分,用来解决空指针异常。
- 接口的增强
- 新的时间和日期API
- Nashorn引擎,允许在JVM上运行JS应用
- 其它新特性,如注解中的重复注解、类型注解,HashMap底层的变化,JVM中Metaspace取代PermGen空间等
- …
新特性的特点
- 速度更快
- 代码更少(增加了新的语法:Lambda 表达式)
- 强大的 Stream API
- 便于并行
- 最大化减少空指针异常:Optional
- …
1. JDK8关于接口
-
在jdk1.8之前,interface中可以定义变量和方法,变量必须是
public
,static
,final
,方法必须是public
,abstract
,这些修饰符都是默认的。接口定义的变量:public
,static
,final
;接口定义的方法:public
,abstract
,抽象方法,需要子类实现。 -
从jdk1.8开始,支持
static
,default
修饰方法体,不需要写子类重写。
现有一个接口Jdk8Interface
,有一个普通方法eat()
,有一个默认方法drink()
,有一个静态方法walk()
。
public interface Jdk8Interface {
/**
* jdk8以前的接口写法,默认是public
*/
void eat();
/**
* jdk8提供的 default默认方法,可以写方法体
*/
default void drink() {
System.out.println("Drink");
}
/**
* jdk8提供的 static静态方法,可以写方法体
*/
static void walk() {
System.out.println("Walk");
}
}
现有一个接口的实现类Jdk8InterfaceImpl
,根据jdk8的特性,我们只需要实现eat()
方法即可,drink()
的实现可选
public class Jdk8InterfaceImpl implements Jdk8Interface{
@Override
public void eat() {
System.out.println("Impl: eat");
}
// @Override
// public void drink() {
// System.out.println("Impl: drink");
// }
}
测试调用
public class Test {
public static void main(String[] args) {
Jdk8Interface jdk8 = new Jdk8InterfaceImpl();
jdk8.eat(); // eat在接口实现类中实现
jdk8.drink(); // drink是默认方法,有默认实现
Jdk8Interface.walk(); // walk是静态方法
}
}
// 输出
Impl: eat
Drink
Walk
2. Lambda表达式
函数式接口与Lambda表达式之间的关系:lambda表达式相当于是一个行为,传入函数式接口中,进来实现各种操作,即行为参数化
Lambda表达式是一个匿名的函数,即没有函数名。
使用lambda的好处:简化调用匿名函数的过程。
在实际应用中,可以使用Lambda表达式
+方法引用
,来使代码变得更加精简。
JDK8内置提供了四大函数接口:
- 消费型接口:消费型接口代表了在一个输入参数上需要进行的操作
- 供给型接口:供给型接口提供一个给定参数类型的结果
- 函数型接口:Function接口接收一个参数,并返回单一的结果
- 断言型接口:Predicate接口是一个布尔类型的函数,该函数只有一个输入参数
2.1 demo
假设现在有一个OrderService
接口,其中有一个getOrder()
方法,正常情况下是定一个OrderServiceImpl
去实现该接口;也可以使用匿名内部类来直接重写并调用;使用lambda表达式则可以更加快捷定义:
public interface OrderService {
/**
* 普通接口方法
*/
void getOrder();
}
OrderServiceImpl
public class OrderServiceImpl implements OrderService{
@Override
public void getOrder() {
System.out.println("OrderService的实现类:getOrder()");
}
}
测试调用
public static void main(String[] args) {
// 1. 接口实现类
OrderService orderService = new OrderServiceImpl();
orderService.getOrder();
// 2. 匿名内部类创建子类
new OrderService() {
@Override
public void getOrder() {
System.out.println("匿名内部类创建子类重写: getOrder()");
}
}.getOrder();
// 3. Lambda表达式写法
OrderService orderService2 = () -> {
System.out.println("Lambda表达式重写: getOrder()");
};
orderService2.getOrder();
// 3.5 Lambda表达式更精简的写法
((OrderService) () -> System.out.println("Lambda表达式重写: getOrder()")).getOrder();
}
例子
// 1. 匿名内部类
new Thread() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " run");
}
}.start();
// 2. Lambda表达式
new Thread(() -> System.out.println(Thread.currentThread().getName() + " run")).start();
2.2 函数式接口
Java中使用Lambda表达式,必须依赖于函数式接口。
函数式接口:在该接口中,只能存在一个抽象方法,该接口称作为函数式接口。
Lambda表达式的规范:
- 依赖于函数式接口(接口中只有一个抽象方法)
- 允许在函数接口中定义
Object
类的方法,如toString()
- 允许使用默认或者静态方法
- 使用
@FunctionalInterface
注解,用于标识该接口为函数式接口
@FunctionalInterface
public interface MyFunctionalInterface {
void get();
/**
* 允许有默认方法
*/
default void defaultAdd() {
System.out.println("I am a default function");
}
/**
* 允许Object类的方法
* @return
*/
String