jdk8的新特性有
- lambda表达式
- 方法引用
- 函数式接口
- 默认方法
- Stream API
- Date Time API
- Optional类
- Nashorn, JavaScript 引擎
- Base64
一、lambda表达式
- 定义:
允许函数作为方法的参数或者说将代码视作数据,更简洁的表示函数式接口的实例
- lambda表达式的语法:
(parameters) -> expression 或 (parameters) ->{ statements; }
当参数只有一个的时候可以省略括号,像这样x -> 2 * x
- lambda表达式的优势:
使代码更加简洁
- 使用lambda表达式需要注意的地方:
不允许声明同名的参数或者局部变量,使用的外部变量必须是final或者隐式final
必须是函数式接口(显示的函数式或者隐式的(FunctionInterface接口))
代码示例:
public class Java8Tester {
public static void main(String args[]){
Java8Tester tester = new Java8Tester();
// 类型声明
MathOperation addition = (int a, int b) -> a + b;
// 不用类型声明
MathOperation subtraction = (a, b) -> a - b;
// 大括号中的返回语句
MathOperation multiplication = (int a, int b) -> { return a * b; };
// 没有大括号及返回语句
MathOperation division = (int a, int b) -> a / b;
System.out.println("10 + 5 = " + tester.operate(10, 5, addition));
System.out.println("10 - 5 = " + tester.operate(10, 5, subtraction));
System.out.println("10 x 5 = " + tester.operate(10, 5, multiplication));
System.out.println("10 / 5 = " + tester.operate(10, 5, division));
// 不用括号
GreetingService greetService1 = message ->
System.out.println("Hello " + message);
// 用括号
GreetingService greetService2 = (message) ->
System.out.println("Hello " + message);
greetService1.sayMessage("Runoob");
greetService2.sayMessage("Google");
}
interface MathOperation {
int operation(int a, int b);
}
interface GreetingService {
void sayMessage(String message);
}
private int operate(int a, int b, MathOperation mathOperation){
return mathOperation.operation(a, b);
}
}
- 底层原理:
使用匿名内部类时,编译会产生一个字节码文件。而lambda表达式并不会生成字节码文件,而是在运行时会生成一个类。
原有类会新增一个方法,这个方法的方法体就是lambda表达式的代码,形成一个新类会重写方法,重写方法会调用新增的方法
二、方法引用
定义:为已有名字的方法提供一个更简洁的lambda表达式。
方法引用分为4种情况,我在一个类中定义了4个方法来区分不同的方法引用:
public class Car {
//Supplier是jdk1.8的接口,这里和lamda一起使用了
public static Car create(final Supplier<Car> supplier) {
return supplier.get();
}
public static void collide(final Car car) {
System.out.println("Collided " + car.toString());
}
public void follow(final Car another) {
System.out.println(this.toString()+" Following the " + another.toString());
}
public void repair() {
System.out.println("Repaired " + this.toString());
}
}
2.1、构造器引用
一种是类构造器引用
它的语法是Class::new,或者更一般的Class< T >::new,示例如下:
final Car car = Car.create( Car::new );//Car::new相当于() -> new Car()
final List< Car > cars = Arrays.asList( car );
另外一种是数组构造器引用
Class[]::new
Function<Integer,Car[]> function = Car[]::new;
Car[] apply = function.apply(5);
System.out.println(Arrays.toString(apply));
2.2、静态方法引用
它的语法是Class::static_method,示例如下:
cars.forEach( Car::collide );//Car::collide相当于curCar -> Car.collide(curCar)
2.3、特定类的任意对象的方法引用
cars.forEach( Car::repair );//Car::repair相当于curCar -> curCar.repair()
2.4、特定对象的方法引用
final Car police = Car.create( Car::new );
cars.forEach( police::follow );//police::follow相当于curCar -> police.follow(curCar)
提示:如果对于你来说方法引用很难理解的话,看看后面等价的lambda表达式,是不是就容易理解多了。如果lambda表达式都理解不了的话,建议你还是先去把匿名内部类和方法重写搞懂。
ps:个人感觉lambda表达式虽然看起来很简洁,但是很难理解,特别是方法引用。难以理解的东西一般都难以维护,所以不建议使用。
三、函数式接口
函数式接口就是只有一个抽象方法,但是可能多个非抽象方法的接口。
lambda表达式只能实现函数式接口的方法。
JDK1.8之前的函数接口
- java.lang.Runnable
- java.util.concurrent.Callable
- java.security.PrivilegedAction
- java.util.Comparator
- java.io.FileFilter
- java.nio.file.PathMatcher
- java.lang.reflect.InvocationHandler
- java.beans.PropertyChangeListener
- java.awt.event.ActionListener
- javax.swing.event.ChangeListener
JDK 1.8 新增加的函数接口:
- java.util.function
java.util.function 它包含了很多类,用来支持 Java的 函数式编程。
接口 | 抽象方法 | 功能描述 |
---|---|---|
Predicate<T> | boolean test(T t); | 断言型;判断函数,返回判断结果true/false |
Supplier<T> | T get() | 供给型;无参,返回一个指定泛型的对象 |
Function<T, R> | R apply(T t) | 方法型;输入一个参数,得到一个结果 |
Consumer<T> | void accept(T t) | 消费型;传入一个指定泛型的参数,无返回值 |
JDK1.8函数式接口Function、Consumer、Predicate、Supplier - 简书
jdk1.8提供的函数式接口总结:
Function接口的抽象方法接收一个T类型的参数,返回一个R类型
Bi是binary(二)的缩写,名字包含Bi的接口有两个参数
名字包含BinaryOperator的接口有两个参数,并且参数类型和返回值类型相同
名字包含UnaryOperator的接口有一个参数,并且参数类型和返回值类型相同
四、默认方法
简单说,默认方法就是接口可以有实现方法,而且不需要实现类去实现其方法。
我们只需在方法名前面加个 default 关键字即可实现默认方法。
public interface Vehicle {
default void print(){
System.out.println("我是一辆车!");
}
}
下面再看看静态默认方法
public interface Vehicle {
default void print(){
System.out.println("我是一辆车!");
}
// 静态方法
static void blowHorn(){
System.out.println("按喇叭!!!");
}
}
五、Stream
思想:使用Stream处理集合,相当于建立了一个工厂流水线, 可以对元素队列进行筛选, 排序,聚合等。
优点:Stream API高效率、简洁的代码。
创建Stream对象的方式:
方式一:
Collection接口实现了stream方法,因此Collection的实现类可以调用stream方法获取Stream对象
方式二:
Stream.of
方式三:
Stream.builder()方法获取builder对象,然后调用build方法获取Stream对象
终结方法collect方法的参数:
Collector.toCollection
Collector.toList
Collector.toSet
分组、多级分组、分区、数据拼接
并行流
获取并行流的两种方式:
stream对象的parallel方法
parallelStream方法
六、Date Time API
java8添加了新的包java.time来对日期和时间做处理。
七、Optional类
Optional 类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
使用Optional优势:这样我们就不用显式进行空值检测。
import java.util.Optional;
public class Java8Tester {
public static void main(String args[]){
Java8Tester java8Tester = new Java8Tester();
Integer value1 = null;
Integer value2 = new Integer(10);
// Optional.ofNullable - 允许传递为 null 参数
Optional<Integer> a = Optional.ofNullable(value1);
// Optional.of - 如果传递的参数是 null,抛出异常 NullPointerException
Optional<Integer> b = Optional.of(value2);
System.out.println(java8Tester.sum(a,b));
}
public Integer sum(Optional<Integer> a, Optional<Integer> b){
// Optional.isPresent - 判断值是否存在
System.out.println("第一个参数值存在: " + a.isPresent());
System.out.println("第二个参数值存在: " + b.isPresent());
// Optional.orElse - 如果值存在,返回它,否则返回默认值
Integer value1 = a.orElse(new Integer(0));
//Optional.get - 获取值,值需要存在
Integer value2 = b.get();
return value1 + value2;
}
}
八、Nashorn JavaScript
Nashorn是一个JavaScript引擎,有了这个引擎,JavaScript可以调用Java,Java也可以调用JavaScript.
九、Base64
Java 8 内置了 Base64 编码的编码器和解码器。
Base64工具类提供了一套静态方法获取下面三种BASE64编解码器:
- 基本:输出被映射到一组字符A-Za-z0-9+/,编码不添加任何行标,输出的解码仅支持A-Za-z0-9+/。
- URL:输出映射到一组字符A-Za-z0-9+_,输出是URL和文件。
- MIME:输出隐射到MIME友好格式。输出每行不超过76字符,并且使用'\r'并跟随'\n'作为分割。编码输出最后没有行分割。