目录
在写这篇学习笔记之前,我特意查看了jdk的版本,现在在官网上JAVA的JDK已经更新到12的版本了。由于工作的原因,一直使用的都是JDK6的版本。所以一直也接触不到JDK新版本特性的内容,利用最近空闲的时候看了看JDK8、9的新特性,确实超乎了我的想象。
接口
接口在jdk8和jdk9中,接口可以定义:
- 常量
- 抽象方法
- 默认方法
- 静态方法
- 私有方法
接口的实现类需要对接口的抽象方法进行覆盖重新,除非接口的实现类是一个抽象类。
默认default方法可写方法体,实现类可以重写也可以不重写,直接调用。
接口默认方法典型的做法就是接口升级。
Lambda表达式
Lambda表达式体验
回想之前,如果我们想开启一个线程输出“Hello World”
方式一(匿名内部类):
public class Demo01 {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Hello World");
}
}).start();
}
}
方式二(实现类的方式):
public class RunnableImpl implements Runnable {
@Override
public void run() {
System.out.println("Hello World");
}
}
public class Demo01 {
public static void main(String[] args) {
RunnableImpl runnableImpl = new RunnableImpl();
new Thread(runnableImpl).start();
}
}
上面提供的两种方式,要实现我们想要的效果,方式一是最简洁,代码量也是最少的了,接下来我们使用Lambda的方式实现我们想要的效果:
public class Demo01 {
public static void main(String[] args) {
/**
* () -> System.out.println("Hello World")
* () 表示参数列表,无参数使用括号表示
* -> 固定格式
* System.out.println("Hello World") 表示Lambda表达式要执行的内容,多行执行内容
* 使用{}将需要执行的代码包裹起来
*/
new Thread(() -> System.out.println("Hello World")).start();
}
}
我们可以看到一行代码就解决了问题,这就是Lambda表达式。
Lambda表达式被称为是函数式变成思想,可以有效的解决面向对象编程思想束缚,在某些场景下,可以快速的帮你实现想要的效果。
面向对象编程思想:强调一切皆对象,如果想要做什么事情,必须找到对象来做。
函数式编程思想:强调做什么,而不是怎么做。
通过上面的例子,使用Lambda表达式与传统写法对比:
1)代码量不同。
2)Lambda表达式代码比较简洁
new Thread()构造函数,传递参数是Runnable接口,通过查看Runnable接口源代码,发现Runnable接口是一个函数式接口
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
按照我目前的理解,Lambda表达式的执行是依赖一个函数式接口。
Lambda表达式标准格式
Lambda表达式3要素:参数、箭头、执行代码构成了表达式
(parameters) -> expression
或者
(parameters) -> { statements;}
参数可以有多个,没有参数直接写 ()
箭头是固定写法
大括号其实就相当于方法体
Lambda表达式使用的前提
- 必须有一个接口(这个接口称为函数式接口)
- 接口当中必须保证有且只有一个抽象方法
Lambda表达式无参数无返回值示例
示例:调用makeFood方法,打印做饭了。
public interface Cook {
void makeFood();
}
public class Demo02 {
public static void main(String[] args) {
method(() -> System.out.println("做饭了!"));
}
public static void method(Cook cook) {
cook.makeFood();
}
}
执行后,控制台打印:
做饭了!
传统的做法:
1)创建实现接口的实现类
2)重写makeFood方法
3)创建实现类对象,调用实现的方法
Lambda表达式的做法:
1)创一个提供类型推断的method方法,这个方法的参数是接口类型
2)调用method方法,使用Lambda表达式作为参数,执行即可。
Lambda表达式有参数有返回值示例
示例:按照年龄排序
public class Person {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
public class Demo03 {
public static void main(String[] args) {
Person[] array = { new Person("张无忌", 22), new Person("赵敏", 21), new Person("张三丰", 58), new Person("谢逊", 100) };
Arrays.sort(array, (Person p1, Person p2) -> {
return p1.getAge() - p2.getAge();
});
System.out.println(Arrays.toString(array));
}
}
在上面的示例中,我们使用的是Comparator接口。
@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2);
boolean equals(Object obj);
@FunctionalInterface
标明@FunctionalInterface该注解的接口,被称为是一个函数式接口,这个注解主要作用是检查我们编写的接口是否是符合函数式接口规范。
我们看到这个Comparator接口抽象方法不止一个,为什么也能称为是函数式接口?
1)默认方法与静态方法并不影响函数式接口的契约
2)可以有 Object 中覆盖的方法,也就是 equals,toString,hashcode等方法
Lambda表达式省略规则
- 参数类型可以省略
- 如果只有一个参数可以省略小括号
- 大括号内只有一条语句,那么大括号、return、分号都可以省略
Lambda表达式赋值推导接口
Lambda表达式必须有上下文推导:
1.调用方法的参数推导得知Lambda对应的接口。
/**
* Thread构造函数接口参数是Runnable函数式接口
*/
new Thread(() -> System.out.println("Hello World")).start();
2.根据局部变量分赋值来推导得知Lambda对应的接口。
/**
* 局部变量的方式
*/
Runnable task = () -> System.out.println("Hello World");
new Thread(task).start();
Lambda表达式与匿名内部类区别
语法糖:代码的写法更加简洁,但其实原理不变,例如方法中的可变参数,但底层仍然是个数组。Lambda表达式和匿名内部类存在根本区别,不是语法糖。
区别:
1)所需的类型不一样:
如果是匿名内部类,那么可以用接口,抽象类,普通的类。
如果是Lambda表发誓,那么只能使用函数式接口
2)使用的限制不同;
接口中只有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类。如果接口中抽象方法不唯一,那么只能使用匿名内部类不能使用Lambda表达式。
3)实现原理不同
匿名内部类,编译后,会产生单独的字节码文件
Lambda表达式,编译后,不会产生字节码文件,对应的字节码文件会在运行的时候动态生成(具体可以参考invokedynamic指令)。