1. lambda表达式
1)概念
(1)Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性
(2)Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中
(3)使用 Lambda 表达式可以使代码变的更加简洁紧凑
2)格式
(parameters) -> expression 或 (parameters) ->{ statements; }
3)特征
(1)可选类型声明:不需要声明参数类型,编译器可以统一识别参数值
(2)可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号
(3)可选的大括号:如果主体包含了一个语句,就不需要使用大括号
(4)可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值
2. 方法引用
@FunctionalInterface
public interface Supplier<T> {
T get();
}
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("Following the " + another.toString());
}
public void repair() {
System.out.println("Repaired " + this.toString());
}
}
1)构造器引用:Class::new,或者更一般的Class< T >::new ,注意:这个构造器没有参数
final Car car = Car.create( Car::new );
final List< Car > cars = Arrays.asList( car );
2)静态方法引用:Class::static_method, 注意:这个方法接受一个Car类型的参数
cars.forEach( Car::collide );
3)特定类的任意对象的方法引用:Class::method ,注意,这个方法没有定义入参
cars.forEach( Car::repair );
4)特定对象的方法引用:instance::method ,注意:这个方法接受一个Car类型的参数
final Car police = Car.create( Car::new );
cars.forEach( police::follow );
3. 函数式接口
1)概念
(1)函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口
(2)函数式接口可以被隐式转换为 lambda 表达式,函数式接口可以对现有的函数友好地支持 lambda
(3)那么就可以使用Lambda表达式来表示该接口的一个实现(注:JAVA 8 之前一般是用匿名类实现的):
@FunctionalInterface
interface GreetingService
{
void sayMessage(String message);
}
GreetingService greetService1 = message -> System.out.println("Hello " + message);
2)关于 @FunctionalInterface 注解
(1)Java 8为函数式接口引入了一个新注解@FunctionalInterface,主要用于编译级错误检查,加上该注解,当你写的接口不符合函数式接口定义的时候,编译器会报错
(2)加不加 @FunctionalInterface 对于接口是不是函数式接口没有影响,该注解只是提醒编译器去检查该接口是否仅包含一个抽象方法
(3)函数式接口里是可以包含默认方法,因为默认方法不是抽象方法,其有一个默认实现,所以是符合函数式接口的定义的
(4)函数式接口里是可以包含静态方法,因为静态方法不能是抽象方法,是一个已经实现了的方法,所以是符合函数式接口的定义的
(5)函数式接口里是可以包含Object里的public方法,这些方法对于函数式接口来说,不被当成是抽象方法(虽然它们是抽象方法);因为任何一个函数式接口的实现,默认都继承了 Object 类,包含了来自 java.lang.Object 里对这些抽象方法的实现
3)java8四大函数式接口
(1)Function<T, R> T:入参类型,R:出参类型
a. 方法 R apply(T t)
b. 示例
Function<Integer, Integer> func = p -> p*10;
Integer result = func.apply(10);
System.out.println("result = " + result);
(2)Consumer<T> T:入参类型;没有出参
a. 方法 void accept(T t)
b. 示例
Consumer<String> consumer = p -> System.out.println("p = " + p);
consumer.accept("消费者你好啊");
(3)Supplier<T> T:出参类型;没有入参
a. 方法 T get()
b. 示例
Supplier<Integer> supplier = () -> 100;
Integer supplierResult = supplier.get();
System.out.println("supplierResult = " + supplierResult);
(4)Predicate<T> T:入参类型;出参类型是Boolean
a. 方法 boolean test(T t)
b. 示例
Predicate<Integer> predicate = p -> p % 2 == 0;
Boolean predicateResult = predicate.test(100);
System.out.println("predicateResult = " + predicateResult);
10. Predicate
c.概念
i. Predicate接口主要用来判断一个参数是否符合要求,类似于Junit的assert
ii. Predicate是一个函数式接口,可以被应用于lambda表达式和方法引用
iii. 可以用Predicate<T>结合lambda表达式来定义一些判断条件
d. 正则表达式表示为Predicate
i. Predicate<String> emailFilter = Pattern.compile("^(.+)@example.com$").asPredicate();
4. 默认方法
1)Java 8 新增了接口的默认方法。简单说,默认方法就是接口可以有实现方法,而且不需要实现类去实现其方法
2)我们只需在方法名前面加个 default 关键字即可实现默认方法
3)Java 8 的另一个特性是接口可以声明(并且可以提供实现)静态方法
4)一个接口有默认方法,考虑这样的情况,一个类实现了多个接口,且这些接口有相同的默认方法,解决方案如下:
(1)第一个解决方案是创建自己的默认方法,来覆盖重写接口的默认方法
(2)第二种解决方案可以使用 super 来调用指定接口的默认方法(比如 Vehicle.super.print())
5)为什么要有这个特性?
(1)当需要修改接口时候,需要修改全部实现该接口的类,目前的 java 8 之前的集合框架没有 foreach 方法,通常能想到的解决办法是在JDK里给相关的接口添加新的方法及实现
(2)然而,对于已经发布的版本,是没法在给接口添加新方法的同时不影响已有的实现。所以引进的默认方法。他们的目的是为了解决接口的修改与现有的实现不兼容的问题
6)由于JVM上的默认方法的实现在字节码层面提供了支持,因此效率非常高。默认方法允许在不打破现有继承体系的基础上改进接口。该特性在官方库中的应用是:给java.util.Collection接口添加新方法,如stream()、parallelStream()、forEach()和removeIf()等等
7)尽管默认方法有这么多好处,但在实际开发中应该谨慎使用:在复杂的继承体系中,默认方法可能引起歧义和编译错误
5. Java 8 Stream (见 stream专篇)
6. Optional 类
1)概念
(1)Optional 类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象
(2)Optional 是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测
(3)Optional 类的引入很好的解决空指针异常
2)常用方法
(1)T get() 如果在这个Optional中包含这个值,返回值,否则抛出异常:NoSuchElementException
(2)boolean isPresent() 如果值存在则方法会返回true,否则返回 false
(3)T orElse(T other) 如果存在该值,返回值, 否则返回 other(传入一个默认值)
(4)static <T> Optional<T> of(T value) 返回一个指定非null值的Optional
(5)static <T> Optional<T> ofNullable(T value) 如果为非空,返回 Optional 描述的指定值,否则返回空的 Optional
3)经典用法
(1)Optional.ofNullable(user).ifPresent(a -> { do(a) }); // 是否为空,不为空则执行lambda表达式
(2)Optional.ofNullable(user).filter( a -> {}).orElseGet(() -> { }) // if不为空else为空的场景
7. Nashorn JavaScript
1)概念
(1)从JDK 1.8开始,Nashorn取代Rhino(JDK 1.6, JDK1.7)成为Java的嵌入式JavaScript引擎
(2)Nashorn完全支持ECMAScript 5.1规范以及一些扩展。它使用基于JSR 292的新语言特性,其中包含在JDK 7中引入的 invokedynamic,将JavaScript编译成Java字节码
(3)与先前的Rhino实现相比,这带来了2到10倍的性能提升
2)jjs
(1)jjs是个基于Nashorn引擎的命令行工具。它接受一些JavaScript源代码为参数,并且执行这些源代码
3)Java 中调用 JavaScript
(1)使用 ScriptEngineManager, JavaScript 代码可以在 Java 中执行
ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
ScriptEngine nashorn = scriptEngineManager.getEngineByName("nashorn");
nashorn.eval("print('" + name + "')"); // 括号里面执行js代码
8. 日期时间 API
1)旧版时间API问题
(1)非线程安全 − java.util.Date 是非线程安全的,所有的日期类都是可变的,这是Java日期类最大的问题之一
(2)设计很差 − Java的日期/时间类的定义并不一致,在java.util和java.sql的包中都有日期类,此外用于格式化和解析的类在java.text包中定义。java.util.Date同时包含日期和时间,而java.sql.Date仅包含日期,将其纳入java.sql包并不合理。另外这两个类都有相同的名字,这本身就是一个非常糟糕的设计
(3)时区处理麻烦 − 日期类并不提供国际化,没有时区支持,因此Java引入了java.util.Calendar和java.util.TimeZone类,但他们同样存在上述所有的问题
2)新版
(1)Java 8 在 java.time 包下提供了很多新的 API。以下为两个比较重要的 API:
a. Local(本地) − 简化了日期时间的处理,没有时区的问题
b. Zoned(时区) − 通过制定的时区处理日期时间
(2)新的java.time包涵盖了所有处理日期,时间,日期/时间,时区,时刻(instants),过程(during)与时钟(clock)的操作
(3)LocalDate/LocalTime 和 LocalDateTime 类可以在处理时区不是必须的情况
(4)如果我们需要考虑到时区,就可以使用时区的日期时间API:ZonedDateTime/ZoneId
9. Base64
1)在Java 8中,Base64编码已经成为Java类库的标准。Java 8 内置了 Base64 编码的编码器和解码器
2)Base64工具类提供了一套静态方法获取下面三种BASE64编解码器:
(1)基本:输出被映射到一组字符A-Za-z0-9+/,编码不添加任何行标,输出的解码仅支持A-Za-z0-9+/
(2)URL:输出映射到一组字符A-Za-z0-9+_,输出是URL和文件
(3)MIME:输出隐射到MIME友好格式。输出每行不超过76字符,并且使用'\r'并跟随'\n'作为分割。编码输出最后没有行分割
3)内嵌类
(1)static class Base64.Encoder 该类实现一个编码器,使用 Base64 编码来编码字节数据
(2)static class Base64.Decoder 该类实现一个解码器用于,使用 Base64 编码来解码字节数据
10. 排序
1)常见排序
(1)Comparable,对于多元素排序,它的排序依据元素是固定的(compareTo()抽象方法只能实现一次,传入一个参数),因此排序方式固定
(2)Comparator,元素的排序依据元素是可变的,因为可以定义N多个外部类,每个外部类实现一种排序。在不同需求下,选择不同的排序(compare()方法是传入两个对比的参数)
2)java8新增排序方法
(1)Collections.sort(employees, (o1, o2) -> o1.getName().compareTo(o2.getName()));
(2)employees.sort((o1, o2) -> o1.getName().compareTo(o2.getName()));
(3)employees.sort(Comparator.comparing(Employee::getName));
11. 其他
1)Java 8引入了重复注解的概念,使用@Repeatable注解定义重复注解,允许在同一个地方多次使用同一个注解
2)Java 8编译器在类型推断方面有很大的提升,在很多场景下编译器可以推导出某个参数的数据类型,从而使得代码更为简洁
3)Java 8拓宽了注解的应用场景。现在,注解几乎可以使用在任何元素上:局部变量、接口类型、超类和接口实现类,甚至可以用在函数的异常定义上
4)Java8版本新增了很多新的方法,用于支持并行数组处理。最重要的方法是parallelSort(),可以显著加快多核机器上的数组排序
5)并发性
(1)基于新增的lambda表达式和steam特性,为Java 8中为java.util.concurrent.ConcurrentHashMap类添加了新的方法来支持聚焦操作;另外,也为java.util.concurrentForkJoinPool类添加了新的方法来支持通用线程池操作
(2)Java 8还添加了新的java.util.concurrent.locks.StampedLock类,用于支持基于容量的锁——该锁有三个模型用于支持读写操作(可以把这个锁当做是java.util.concurrent.locks.ReadWriteLock的替代者)
(3)在java.util.concurrent.atomic包中也新增了不少工具类
6)使用Metaspace(JEP 122)代替持久代(PermGen space)。在JVM参数方面,使用-XX:MetaSpaceSize和-XX:MaxMetaspaceSize代替原来的-XX:PermSize和-XX:MaxPermSize
参考网址
java8 四大核心函数式接口Function、Consumer、Supplier、Predicate
注:文章是经过参考其他的文章然后自己整理出来的,有可能是小部分参考,也有可能是大部分参考,但绝对不是直接转载,觉得侵权了我会删,我只是把这个用于自己的笔记,顺便整理下知识的同时,能帮到一部分人。
ps : 有错误的还望各位大佬指正,小弟不胜感激