上一篇【SpringBoot】二、SpringBoot自动配置原理
Java 8新特性
新时代的Java语言
新时代的Java是以JDK8的发布为分水岭,从JDK8开始,Java开发进入一个新的阶段,Java8 经历9个里程碑版本,于2014年3月18日正式发布,距今已有很长一段时间了,很多企业已经逐步迁移Java8,Java8有非常重大的变化,新增了很多新的语法与特性; 一些流行框架都采用了Java8,比如Mybatis3.5.x开始全面采用JDK8,Spring5.x开始全面采用JDK8,SpringBoot2.x开始全面采用JDK8,Spring Cloud F版开始也要求JDK8;
Java8的一些新变化统计
- 195新的文件增加到了JDK8 (目前JDK总共4240文件);
- 93个新类, 89新接口, 13新的枚举;
- 2699新方法, 56新的构造函数,49新属性;
- 46接口被注解为@FunctionalInterface(函数式接口);
- 213接口默认方法;
- 68静态接口方法;
Java8有哪些新特性?
- 1、Lambda表达式;
- 2、方法引用;
- 3、默认方法;
- 4、可重复注解;
- 5、类型注解;
- 6、改进的类型推断;
- 7、方法参数反射;
- 8、Stream API;
- 9、Security增强;
- 10、JavaFX增强;
- 11、Tool增强;
- 12、国际化增强;
- 13、Deployment增强;
- 14、date-time日期包;
- 15、Nashorn javascript引擎;
- 16、Pack200压缩工具;
- 17、IO and NIO增强;
- 18、java.lang and java.util 包增强;
- 19、JDBC 4.2增强;
- 20、Java DB;
- 21、java.net包增强;
- 22、java.util.concurrent包增强;
- 23、Java XML - JAXP;
- 24、HotSpot增强;
- 25、Java Mission Control;
- 26、Optional类
接口默认方法
接口不仅可以声明未实现的抽象方法,也可以声明已经实现了的方法,而不需要实现类去实现其方法; 默认方法就像一个普通Java方法,只是方法的修饰符用default关键字; 接口里面的默认方法,子类可以覆盖; 接口里面的静态方法,子类不能覆盖;
接口静态方法
接口中可以定义静态方法,它与普通的静态方法没有区别; 静态方法就像一个普通Java静态方法,但方法的关键词只能是public或者不写;
为什么要新增该特性?
如果JDK在java8时,在List接口中新增了一个抽象方法,而我们项目写了一个类实现了List接口,由于项目是之前采用jdk7开发的,那么当我们项目采用jdk8运行时,就会报错,因为你没有实现List里面新的抽象方法; 所以Java设计人员引入了接口默认方法,其目的是为了解决接口的修改与已有的实现不兼容的问题,接口默认方法可以作为库、框架向前兼容的一种手段;
函数式接口
- 1、只包含一个方法的接口(有且只有一个方法,并且该方法必须是抽象方法)叫函数式接口;
- 2、Java标准库中的java.lang.Runnable,java.util.concurrent.Callable就是典型的函数式接口;
- 3、在Java 8中通过@FunctionalInterface注解,将一个接口标注为函数式接口,该接口只能包含一个抽象方法;
- 4、@FunctionalInterface注解不是必须的,只要接口只包含一个抽象方法,虚拟机会自动判断该接口为函数式接口;
- 5、一般建议在接口上使用@FunctionalInterface注解进行声明,以免他人错误地往接口中添加新方法,如果在你的接口中定义了第二个抽象方法的话,编译器会报错;
- 6、但是标记了@FunctionalInterface注解的接口可以有一个或多个默认接口方法和静态方法,比如Java类库中的: java.util.Comparator;
7、函数式接口里允许定义java.lang.Object里的public方法(非final方法)
- Java 8中类库中许多接口都添加了@FunctionalInterface注解,比如:
java.lang.Runnable,
java.util.Comparator,
java.util.concurrent.Callable
java.util.function包下的所有接口;
- 8、函数式接口是为Java 8中的lambda而设计的,lambda表达式的方法体其实就是函数接口的实现;
Lambda表达式
public void test () {
。。。。。
}
Lambda 表达式是一个匿名方法,它是一个函数式接口的具体实现方式; 也就是说一个函数式接口都可以用Lambda表达式来实现; 所以任意只包含一个抽象方法的接口(函数式接口),我们都可以用lambda表达式去编写;
Lambda表达式语法结构
包含三个部分
public void aa (String s, int a) {
。。。。。
。。。。。
}
(String s, int a) -> {
。。。。。
。。。。。
}
1、一个括号内用逗号分隔的形式参数,参数是函数式接口里面方法的参数; 2、一个固定的箭头符号:-> 3、一个方法体,方法体可以是表达式,也可以是代码块,方法体是函数式接口里面方法的具体实现;
总体结构如下:
(parameters) -> expression 或者 (parameters) -> { statements; }
- 1、括号里的形式参数类型可以省略,编译器会根据上下文来推测参数的类型,你也可以显式地指定参数类型;
- 2、如果没有形式参数,则括号里空着;
- 3、方法体,如果有多行功能语句用大括号括起来,如果只有一行功能语句则可以省略大括号,(一般地,如果Lambda的功能语句块比较复杂,我们都用大括号包起来);
- 4、如果Lambda方法体只有一行return语句,则如果省略了大括号,那么return可以省略;
- 5、如果参数列表只有一个参数,则参数列表的小括号可以省略;
内置功能性函数式接口
比较典型的几个 Java8的内置功能性函数式接口在java.util.function包下,主要有四大核心的内置功能型函数式接口:
- 谓词( Predicate)
- 函数( Function)
- 生产者( Supplier)
- 消费者( Consumer)
1、谓词:(Predicate)
谓词是单参数的返回布尔值的函数式接口,输入一个参数,返回true或者false;
2、函数:(Function)
Function接受一个参数,并产生一个结果;
3、生产者:(Supplier)
Suppliers产生一个给定的泛型类型的结果。与Function不同的是Suppliers不接受输入参数;
4、消费者:(Consumer)
Consumers代表在一个单一的输入参数上执行操作,Consumer的操作可能会更改输入参数的内部状态;
方法引用
方法引用是lambda表达式的一种简写形式; 方法引用是用来直接访问类或者实例的已经存在的方法或者构造方法; 如果lambda表达式只是调用一个特定的已经存在的方法,则可以使用方法引用; 如果你觉得lambda的方法体代码很长,影响代码可读性,则可以用方法引用来解决; 语法:
使用 :: 操作符将方法名与对象或类的名字分隔开;
优雅的Optional类
来自官方的Optional 介绍: A container object which may or may not contain a non-null value. If a value is present, isPresent() will return true and get() will return the value. Optional 类是一个可以为null的容器对象,如果值存在则isPresent()方法会返回true,否则为false,调用get()方法会返回该对象; Optional 这个容器可以保存泛型类型T的值,或者仅仅保存null,Optional提供很多有用的方法:
Java8中Optional的引入,这样方便我们进行空值检测; Optional 类的引入很好的解决空指针异常问题,使得开发避免了大量Null指针的出现,借助相关方法避免了if else这种繁琐的逻辑代码编写,对于在处理空的场景下应用较多,对于if else的逻辑场景,使用Optional让程序更加简洁,同时使用Optional可以实现代码的链式处理; 使用 Optional 具有如下优点: (1)将防御式编程代码完美包装;少一些if else (2)链式调用; (3)有效避免程序代码中的空指针; 但是也同样具有一些缺点: (1)流行性不是非常理想,但是是趋势,团队新成员需要学习成本; (2)有时候代码阅读看起来很不习惯; 除了Optional,还有OptionalDouble、OptionalInt、OptionalLong;
Stream Api
文档可以参考:https://space.bilibili.com/551304439/article
1、集合处理数据的弊端
当我们在需要对集合中的元素进行操作的时候,除了表的添加,删除、获取外,最典型的操作就是集合的遍历
2、Stream流式思想概述
注意:Stream和IO流(InputStream/OutputStream)没有任何关系,请暂时忘记对传统IO流的固有印象! Stream流式思想类似于工厂车间的“生产流水线”,Stream流不是一种数据结构,不保存数据,而是对数据进行加工 处理。Stream可以看作是流水线上的一个工序。在流水线上,通过多个工序让一个原材料加工成一个商品。
Stream API能让我们快速完成许多复杂的操作,如筛选、切片、映射、查找、去除重复,统计,匹配和归约。 小结 :首先我们了解了集合操作数据的弊端,每次都需要循环遍历,还要创建新集合,很麻烦 Stream是流式思想,相当于工厂的流水线,对集合中的数据进行加工处理
3、获取Stream流的两种方式
获取一个流非常简单,有以下几种常用的方式:
- 1、所有的 Collection 集合都可以通过 stream 默认方法获取流;
- 2、Stream 接口的静态方法 of 可以获取数组对应的流
方式1 : 根据Collection获取流 方式2 : Stream中的静态方法of获取流
4、Stream流常用方法操作
Stream常用方法 Stream流模型的操作很丰富,这里介绍一些常用的API。这些方法可以被分成两种:
5、Stream流的map方法
如果需要将流中的元素映射到另一个流中,可以使用 map 方法。方法签名:
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
该接口需要一个 Function 函数式接口参数,可以将当前流中的T类型数据转换为另一种R类型的流。 Stream流中的 map 方法基本使用的代码如:
@Test
public void testMap() {
Stream<String> original = Stream.of("11", "22", "33");
Stream<Integer> result = original.map(Integer::parseInt);
result.forEach(s -> System.out.println(s + 10));
}
这段代码中, map 方法的参数通过方法引用,将字符串类型转换成为了int类型(并自动装箱为 Integer 类对象)。可以参考:木子教程 https://www.bilibili.com/read/cv14222552?spm_id_from=333.999.0.0
|