1. JDK 版本变更
每6个月发布一个小版本,每3年发布一个长期支持的版本。
版本 | 发布日期 | 延长日期 | 长期支持 |
---|---|---|---|
JDK 7 | 2011年9月 | 2022年9月 | LTS |
JDK 8 | 2014年3月 | 2030年12月 | LTS |
JDK 9 | 2017年9月 | ||
JDK 10 | 2018年3月 | ||
JDK 11 | 2018年9月 | 2026年9月 | LTS |
JDK 12 | 2019年3月 | ||
JDK 13 | 2019年9月 | ||
JDK 14 | 2020年3月 | ||
JDK 15 | 2020年9月 | ||
JDK 16 | 2021年3月 | ||
JDK 17 | 2021年9月 | 2029年9月 | LTS |
JDK 支持路线图:
https://www.oracle.com/java/technologies/java-se-support-roadmap.html
Java 8-15新特性:
https://my.oschina.net/mdxlcj/blog/3009128
2. JDK 8 新特性
特性 | 说明 |
---|---|
接口改变 | 接口中可以有默认方法和静态方法 |
函数式接口 | 只有一个抽象方法的接口 |
Lambda表达式 | 基于函数式接口;简化代码,提高开发效率 |
方法引用 | :: 操作符调用方法,不需要传参 |
Stream API | 使得对集合的操作更加简单方便 |
Optional | 最大化减少空指针异常 |
Nashorn引擎 | 允许在JVM上运行JS |
2.1 接口方法变换
- 接口中可以有默认方法
- 接口中可以有静态方法
public interface Interface {
default void defaultMethod(){
System.out.println("JDK 8 新特性:接口中可以有默认方法");
}
static void staticMethod(){
System.out.println("JDK 8 新特性:接口中可以有静态方法");
}
}
2.2 函数式接口
- 定义:只有一个抽象方法的接口
@FunctionalInterface
public interface FuncationalInterface {
void test();
// 加入下面方法后注解报错
// void test3();
default void test1(){
System.out.println("default");
}
static void test2(){
System.out.println("static");
}
}
- JDK提供的函数式接口:
接口 | 方法 | 方法说明 |
---|---|---|
Supplier | T get() | 供应接口:不需要参数,按照某种实现逻辑(Lambda表达式)返回一个数据 |
Consumer | void accept(T t) | 对传入的参数进行操作,具体操作由 Lambda 表达式实现 |
Consumer | 返回一个组合的Consumer,依次执行此操作,然后执行after操作 | |
Predicate | boolean test(T t) | 对给定的参数进行判断,返回 true 或 false |
Predicate | 返回一个逻辑的否定,对应 逻辑非(!) | |
Predicate | 返回一个组合判断,对应 短路与(&&) | |
Predicate | 返回一个组合判断,对应 短路或(||) | |
Function<T, R> | R apply(T t) | 将此参数应用于给定的参数,数据处理转换 |
Function andThen(Function fun) | 返回一个组合函数,首先将该函数应用于输入,然后将 fun 应用于结果 |
- Supplier
接口
// 编写调用 Supplier<T> 接口 get() 方法的方法
private static String getString(Supplier<String> sup){
return sup.get();
}
// 测试
public static void main(String[] args) {
String s1 = getString(() -> "AAA");
System.out.println(s1);
}
- Consumer
接口
// 定义一个消费字符串的方法
private static void optString1(String name, Consumer<String> con){
con.accept(name);
}
// 定义一个消费字符串的方法,用不同的方式消费字符串两次
private static void optString2(String name, Consumer<String> con1, Consumer<String> con2){
con1.accept(name);// 第一次操作
con2.accept(name);// 第二次操作
}
// 定义一个消费字符串的方法,用不同的方式消费字符串两次 对上面方法的改进
private static void optString3(String name, Consumer<String> con1, Consumer<String> con2){
// 等同于:con1.accept(name); con2.accept(name);
con1.andThen(con2).accept(name);
}
// 测试
public static void main(String[] args) {
// 对字符串进行具体的操作,此处对字符串做了倒叙输入操作
optString1("ABC", name -> System.out.println(new StringBuilder(name).reverse().toString()));
// 此处第一次消费是直接输入字符串,第二次消费是倒叙再输出
optString3("ABC", name -> System.out.println(name),
name -> System.out.println(new StringBuilder(name).reverse().toString()));
}
- Predicate
接口
// test()方法:判断给定的字符串是否满足条件
private static boolean checkStr(String s, Predicate<String> pre){
return pre.test(s);
}
// negate()方法:判断给定的字符串是否满足条件
private static boolean checkStr1(String s, Predicate<String> pre){
// 调用判断方法,具体判断逻辑由 Lambda 表达式实现
// 等同于:!pre.test(s);
// negate()方法底层实现就是对test()方法做了 !test() 取反操作
return pre.negate().test(s);
}
// and():定义方法,对给定的字符串做两次不同的判断,然后将两次判断的结果 逻辑与 后作为最终结果返回
private static boolean checkStr2(String s, Predicate<String> pre1, Predicate<String> pre2){
// 调用判断方法,具体判断逻辑由 Lambda 表达式实现
//boolean b1 = pre1.test(s);
//boolean b2 = pre2.test(s);
//boolean b = b1 && b2;
//return b;
// 等同于上面的操作
return pre1.and(pre2).test(s)
}
// or():定义方法,对给定的字符串做两次不同的判断,然后将两次判断的结果 逻辑或 后作为最终结果返回
private static boolean checkStr3(String s, Predicate<String> pre1, Predicate<String> pre2){
// 调用判断方法,具体判断逻辑由 Lambda 表达式实现
//boolean b1 = pre1.test(s);
//boolean b2 = pre2.test(s);
//boolean b = b1 || b2;
//return b;
// 等同于上面的操作
return pre1.or(pre2).test(s);
}
// 测试
public static void main(String[] args) {
// test()方法:判断字符串长度是否大于 8
boolean b1 = checkStr("hello", str -> str.length() > 8);
System.out.println(b1);
// and()方法:判断字符串长度是否大于 8 并且小于 15
boolean b4 = checkStr2("hello", str -> str.length() > 8, str -> str.length() < 15);
System.out.println(b4);
}
// 给定一个字符数组,将名字长度大于 3 年龄小于 35 的筛选出来
private static List<String> check(String[] strArray, Predicate<String> pre1, Predicate<String> pre2){
List<String> list = new ArrayList<>();
for(String str : strArray){
if(pre1.and(pre2).test(str)){
list.add(str);
}
}
return list;
}
// 实现判断逻辑
// 判断名字长度大于 2 并且 年龄大于 33 的
public static void main(String[] args) {
String [] array = {"zhangsan,20", "lisi,39","wu,35","zl,36"};
List<String> list1 = check(array, str -> str.split(",")[0].length() > 2, str -> Integer.parseInt(str.split(",")[1]) > 33);
for(String s : list){
System.out.println(s);
}
}
- Function<T, R> 接口
// apply() 方法1: 定义一个方法,将字符串转换成 int
private static void convert(String str, Function<String, Integer> fun){
// 具体转换逻辑由 Lambda 实现
int i = fun.apply(str);
System.out.println(i);
}
//apply() 方法2:定义一个方法,把一个int类型的数据加上1个整数之后,转为字符串在控制台输出
private static void convert(int i, Function<Integer, String> fun){
String s = fun.apply(i);
System.out.println(s);
}
//addThen() 方法:定义一个方法,把一个 String 类型的数据转换成 int 加上 1 个整数之后,转为字符串在控制台输出
private static void convert(String s, Function<String, Integer> fun1, Function<Integer, String> fun2){
//Integer i = fun1.apply(s);
//String str = fun2.apply(i);
// 等同于上面
String str = fun1.andThen(fun2).apply(s);
System.out.println(str);
}
// Lambda 实现
public static void main(String[] args) {
// 将一个字符串转换成 int
convert("100", str -> Integer.parseInt(str));
// 方法引用实现
convert("100", Integer::parseInt);
//把一个int类型的数据加上1个整数之后,转为字符串
convert(100, i -> String.valueOf(i + 10));
//定义一个方法,把一个 String 类型的数据转换成 int 加上 1 个整数之后,转为字符串在控制台输出
convert("100", str -> Integer.parseInt(str), i -> String.valueOf(i + 10));
}
2.3 Lambda 表达式
本质:就是函数式接口对象
使用前提:
- 必须要有接口;
- 且接口必须是一个函数式接口;
- 必须有上下文环境,这样才能推导出Lambda对应的接口。
语法格式:
(参数1, 参数2, ...) -> { //业务逻辑 ... }; 【注意】:-> 之前不能有空格
省略规则:
- 参数类型可以省略,但是有多个参数情况下,不能只省略一个,要全部省略;
- 如果参数有且只有一个,那么小括号可以省略;
- 如果代码块的语句只有一句,那么可以省略大括号和分号,以及return
举例说明:
STEP1:定义函数式接口 MyInterface1
@FunctionalInterface //此注解可以省略
public interface MyInterface1 {
// 求和
int add(int x, int y);
}
STEP2:定义函数式接口 MyInterface2
@FunctionalInterface //此注解可以省略
public interface MyInterface2 {
void test(String s);
}
STEP3:测试类
public class Test {
// 声明一个方法,调用 MyInterface1 接口中的方法,此方法参数为接口本身
private static void useAdd(MyInterface1 mi1){
mi1.add(1,2);
}
// 声明一个方法,调用 MyInterface2 接口中的方法,此方法参数为接口本身
private static void useTest(MyInterface2 mi2){
mi2.test("Hello Java");
}
public static void main(String[] args) {
// 匿名内部类方式 MyInterface1
useAdd(
new MyInterface() {
@Override
public int add(int x, int y) {
// 此处为接口的实现代码
return x + y;
}
}
);
// 匿名内部类方式 MyInterface2
useTest(new MyInterface2() {
@Override
public void test(String s) {
// 此处为接口的实现代码
System.out.println(s);
}
});
// MyInterface Lambda 表达式方式 1:无任何省略
useAdd((int x, int y) -> {
return x + y;
});
// MyInterface Lambda 表达式方式 2:省略参数类型
useAdd((x, y) -> {
return x + y;
});
// MyInterface Lambda 表达式方式 3:省略参数类型和大括号以及分号
// 如果代码块只有一句代码,那么大括号,分号,return(有返回值的情况) 可以省略
useAdd((x, y) -> x + y);
// MyInterface2 Lambda 表达式方式 1:无任何省略
useTest((String s) -> {
System.out.println(s);
});
// MyInterface2 Lambda 表达式方式 2:省略参数类型
// 如果方法只有一个参数,那么参数类型,小括号可以省略
useTest((s) -> {
System.out.println(s);
});
// MyInterface2 Lambda 表达式方式 3:省略参数类型,大括号等
// 如果代码块只有一句代码,那么大括号,分号,return(有返回值的情况) 可以省略
// 如果方法只有一个参数,那么参数类型,小括号可以省略
useTest((s) -> System.out.println(s));
// MyInterface2 Lambda 表达式方式 4:省略参数类型,大括号,小括号等
// 如果代码块只有一句代码,那么大括号,分号,return(有返回值的情况) 可以省略
// 如果方法有且只有一个参数,那么参数类型,小括号可以省略
useTest(s -> System.out.println(s));
}
}
2.4 Stream流
生成方式:
-
default Stream
stream() Conllection 体系的集合可以使用默认方法 stream() 生成流
-
Map 体系的集合间接的生成流
-
数组可以使用 Stream 接口的静态方法 of(T... values) 生成流
集合类型 | 生成方式 |
---|---|
List | 串行流:list.stream();并行流:list.parallelStream(); |
Set | 串行流:set.stream();并行流:set.parallelStream(); |
Map<K, V> | map.keySet().stream();map.values().stream();map.entrySet().stream() |
T [] | Stream.of(T... t) |
// Conllection 体系的集合生成流的方式
List<String> list = new ArrayList<>();
Stream<String> listStream = list.stream();
Set<String> set = new HashSet<>();
Stream<String> setStream = set.stream();
// Map 体系的集合间接生成流的方式
// 将 Map 先转成 Set 或 EntrySet 再生成对应的流
Map<String, Integer> map = new HashMap<>();
// 生成 key 的流
Stream<String> keyStream = map.keySet().stream();
// 生成 value 的流
Stream<Integer> valueStream = map.values().stream();
// 生成 key - value 的流
Stream<Map.Entry<String, Integer>> entryStream = map.entrySet().stream()
// 数组生成流的方式:Stream 接口的 of(T... values)
// Stream 接口的 of(T... values) 接受的是可变参数
String [] strArray = {"AAA", "BBB", "CCC"};
Stream<String> strArrayStream = Stream.of(strArray);
Stream<String> strStream = Stream.of("AAA", "BBB", "CCC");
Stream<Integer> intStream = Stream.of(10, 20, 30);
方法列表:
方法 | 说明 | 备注 |
---|---|---|
Stream | 过滤流:筛选出符合Lambda表达式的元素 | 中间操作 |
Stream | 截断流:使其元素不超过给定对象 | 中间操作 |
Stream | 跳过元素,返回一个扔掉了前n个元素的流,若流中元素不足n个, 则返回一个空流,与 limit 互补 | 中间操作 |
Stream | 通过流所生成元素的hashCode()和equals去除重复元素 | 中间操作 |
将元素转换成其他形式或提取信息。接受一个函数作为参数, 该函数会被应用到每个元素上,并将其映射成一个新的元素 | 中间操作 | |
各个数组并不是分别映射成一个流,而是映射成流的内容。一对多 | 中间操作 | |
Stream | 自然排序 | 中间操作 |
Stream | 定制排序 | 中间操作 |
boolean allMatch(Predicate | 检查是否匹配所有元素 | 终结操作 |
boolean anyMatch(Predicate | 检查是否至少匹配一个元素 | 终结操作 |
boolean noneMatch(Predicate | 检查是否没有匹配所有元素 | 终结操作 |
Optional | 返回第一个元素 | 终结操作 |
Optional | 返回当前流中的任意元素 | 终结操作 |
long count() | 返回流中元素的总个数 | 终结操作 |
Optional | 返回流中最大值 | 终结操作 |
Optional | 返回流中最小值 | 终结操作 |
T reduce(T identity, BinaryOperator | 归约操作可以将流中元素反复结合起来,得到一个值 | 终结操作 |
R collect(Collector<T, A, R> collector) | 收集:接收一个Collector接口实现,用于给Stream中汇总的方法 | 终结操作 |
Object[] toArray() | 收集:返回一个数组 | 终结操作 |
void forEach(Consumer | 遍历流中的元素 | 终结操作 |
void forEachOrdered(Consumer<? super T> action) | 顺序遍历:主要体现在并行流中 | 终结操作 |
常见中间操作:
- Stream
filter(Predicate pre):用于对流的数据进行过滤 通过 Predicate 接口中的 boolean test(T t):对给定的参数进行判断 - Stream
limit(long maxSize):返回此流中的元素组成的流,截取前指定参数个数的数据 - Stream
skip(long n):跳过指定参数个数的数据,返回由该流的剩元素组成的流 - static
Stream concat(Stream a, Stream b):合并a和b两个流为一个流 - Stream
distinct(): 返回由该流的不同元素(根据Objectequals(Object) )组成的流 Stream map(Function mapper):返回由给定函数应用于此流的元素的结果组成的流Function接口中的方法:R apply(T t) Stream flatMap(Function mapper):一对多,map() 和 flatMap() 区别可以对比 add() 和 addAll() 方法
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("ACS");
list.add("SDFG");
list.add("DCFVB");
list.add("AS");
list.add("ACX");
// 取前 3 个数
list.stream().limit(3).forEach(System.out::println);
System.out.println("-----------");
// 跳过前 3 个元素,取剩下的元素
list.stream().skip(3).forEach(System.out::println);
System.out.println("-----------");
// 跳过前 2 个元素,取剩下的元素的前 2 个
list.stream().skip(2).limit(2).forEach(System.out::println);
// 取前 4 个组成一个流
Stream<String> s1 = list.stream().limit(4);
// 跳过前 2 个组成一个流
Stream<String> s2 = list.stream().skip(2);
// 合并 s1 和 s2
Stream.concat(s1, s2).forEach(System.out::println);
System.out.println("-----------");
// 合并 s1 和 s2 去除里面重复的元素
Stream.concat(s1, s2).distinct().forEach(System.out::println);
}
常见终结操作:
-
void forEach(Consumer con):对留的每个元素操作
Consumer 接口中的方法:void accept(T t):对给定的参数执行此操作
-
long count():返回流中的元素个数
-
R collect(Conllector con):收集元素,但是此方法的参数是一个Conllector接口
因此 Java 提供了Conllctors 工具类供我们将 Stream 流操作后的数据收集到集合中
- public static
Conllector toList():把元素收集到 List 集合中 - public static
Conllector toSet():把元素收集到 Set 集合中 - public static Conllector toMap(Function key, Function value):把元素收集到 Map 集合中
- public static
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("AAA");
list.add("BBBB");
list.add("CCC");
list.add("DDDDD");
// 得到长度为 3 的元素
Stream<String> listStream = list.stream().filter(s -> s.length() == 3);
// 将上面操作得到的流收集到 List 集合中
List<String> retList = listStream.collect(Collectors.toList());
System.out.println(retList);
Set<String> set = new HashSet<>();
set.add("AAA");
set.add("BBBB");
set.add("CCC");
set.add("DDDD");
// 得到长度为 3 的元素
Stream<String> setStream = set.stream().filter(s -> s.length() == 3);
// 将上面操作得到的流收集到 List 集合中
Set<String> retSet = setStream.collect(Collectors.toSet());
System.out.println(retSet);
// 定义一个字符串数组,每一个字符串数据由姓名数据和年龄数据组合而成
String[] strArray = {"AAA,30", "BBB,35", "CCC,33", "D,25"};
// 得到年龄大于 28 的流
Stream<String> mapStream = Stream.of(strArray).filter(s -> Integer.parseInt(s.split(",")[1]) > 28);
// 把使用Stream流操作完毕的数据收集到Map集合中并遍历,字符串中的姓名作键,年龄作值
// toMap(Function key, Function value):需要两个 Lambda 表达式
Map<String, Integer> map = mapStream.collect(Collectors.toMap(
s -> s.split(",")[0],
s -> Integer.parseInt(s.split(",")[1])
));
System.out.println(map);
}
2.5 时间API
- LocalDate
- LocalTime
- LocalDateTime
- DateTimeFormatter
简单好用,线程安全
LocalDateTime localDateTime = LocalDateTime.now();
String format = localDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
System.out.println(format);
2.6 Optional
主要解决的问题是空指针异常
方法 | 描述 |
---|---|
of | 把指定的值封装为Optional对象,如果指定的值为null,则抛出NullPointerException |
empty | 创建一个空的Optional对象 |
ofNullable | 把指定的值封装为Optional对象,如果指定的值为null,则创建一个空的Optional对象 |
get | 如果创建的Optional中有值存在,则返回此值,否则抛出NoSuchElementException |
orElse | 如果创建的Optional中有值存在,则返回此值,否则返回一个默认值 |
orElseGet | 如果创建的Optional中有值存在,则返回此值,否则返回一个由Supplier接口生成的值 |
orElseThrow | 如果创建的Optional中有值存在,则返回此值,否则抛出一个由指定的Supplier接口生成的异常 |
filter | 如果创建的Optional中的值满足filter中的条件,则返回包含该值的Optional对象,否则返回一个空的Optional对象 |
map | 如果创建的Optional中的值存在,对该值执行提供的Function函数调用 |
flagMap | 如果创建的Optional中的值存在,就对该值执行提供的Function函数调用,返回一个Optional类型的值,否则就返回一个空的Optional对象 |
isPresent | 如果创建的Optional中的值存在,返回true,否则返回false |
ifPresent | 如果创建的Optional中的值存在,则执行该方法的调用,否则什么也不做 |
//创建一个值为张三的String类型的Optional
Optional<String> ofOptional = Optional.of("张三");
//如果我们用of方法创建Optional对象时,所传入的值为null,则抛出NullPointerException如下图所示
Optional<String> nullOptional = Optional.of(null);
//为指定的值创建Optional对象,不管所传入的值为null不为null,创建的时候都不会报错
Optional<String> nullOptional = Optional.ofNullable(null);
Optional<String> nullOptional = Optional.ofNullable("lisi");
Optional<String> stringOptional = Optional.of("张三");
System.out.println(stringOptional.orElse("zhangsan"));//张三
Optional<String> emptyOptional = Optional.empty();
System.out.println(emptyOptional.orElse("李四"));//李四
//如果创建的Optional中有值存在,则返回此值,否则返回一个由Supplier接口生成的值
Optional<String> stringOptional = Optional.of("张三");
System.out.println(stringOptional.orElseGet(() -> "zhangsan"));//张三
Optional<String> emptyOptional = Optional.empty();
System.out.println(emptyOptional.orElseGet(() -> "orElseGet"));//orElseGet
//项目实例
public void invokeHead(Map<Integer, CellData> headMap, AnalysisContext context) {
String cardNo = Optional.ofNullable(headMap.get(0)).map(CellData::getStringValue).orElse("");
String cardPassword = Optional.ofNullable(headMap.get(1)).map(CellData::getStringValue).orElse("");
if (!CARD_NO_VALUE.equals(cardNo) || !CARD_PWD_VALUE.equals(cardPassword)) {
throw new BusinessException(CardErrorEnum.import_card_error);
}
}
3. JDK 9 新特性
特性 | 说明 |
---|---|
模块化系统 | module 实现模块之间的相互调用 |
jShell命令 | 交互方式编写、运行Java代码 |
接口的私有方法 | 接口中可以有私有方法 |
钻石操作符的升级 | 匿名内部类中可以省略泛型 |
try...catch语句块的改变 | 自动关闭流 |
String底层存储结构变更 | char[] ----> byte[] |
集合的of() | 创建不可变集合,Arrays.asList(T... t) |
Stream API 的增强 | 新增4个方法 |
目录结构的改变 | 取消了jre目录 |
3.1 模块化:module
在模块下分别创建 module-info.java 文件,放在 src 目录下
提供方:
module ModuleTest{
exports com.example.jdk9;//暴露自己需要提供的包或类名
}
调用方:
module ModuleTest{
requires jdk9-module;//提供方的模块名
}
3.2 jShell命令
CMD 编写Java并允许Java代码
3.3 接口改变
public interface Interface {
private static void test(){
System.out.println("接口中的私有方法");
}
private void test1(){
System.out.println("接口中的私有方法");
}
}
3.4 try...catch语句
- JDK 8 中实现资源的自动关闭,要求所有资源必须在try子句中初始化
try(InputStream inputStream = new FileInputStream("D:\\")){
//////
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
- JDK 9 中的升级,将要关闭的资源放在 try() 即可
InputStream inputStream = new FileInputStream("D:\\");
try(inputStream) {
//////////
}catch (IOException e){
e.printStackTrace();
}
3.5 只读集合创建
- Collections 工具类
// 创建不可变 List 集合
List<String> list = new ArrayList<>();
Collection<String> collectionList = Collections.unmodifiableCollection(list);
List<String> list1 = Collections.unmodifiableList(list);
// 创建不可变 Map 集合
Map<String, String> map = new HashMap<>();
Map<String, String> map1 = Collections.unmodifiableMap(map);
// 创建不可变 Set 集合
Set<String> set = new HashSet<>();
Set<String> set1 = Collections.unmodifiableSet(set);
Collection<String> collectionSet = Collections.unmodifiableCollection(set);
- 集合自身:of()
// 创建不可变 List 集合
List<String> list = List.of("AA", "BB", "CC");
// 创建不可变 Set 集合
Set<String> set = Set.of("AA", "BB", "CC");
// 创建不可变 Map 集合
Map<String, String> map1 = Map.of("k1", "v1", "k2", "v2");
Map<String, Integer> map2 = Map.ofEntries(Map.entry("k1", 1), Map.entry("k2", 2));
3.6 InputStream
新增方法:long transferTo(OutputStream out),不需要我们自己写循环读取流
public long transferTo(OutputStream out) throws IOException {
Objects.requireNonNull(out, "out");
long transferred = 0;
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];//DEFAULT_BUFFER_SIZE = 8192
int read;
while ((read = this.read(buffer, 0, DEFAULT_BUFFER_SIZE)) >= 0) {
out.write(buffer, 0, read);
transferred += read;
}
return transferred;
}
3.7 Stream API
新增了如下4个方法
方法 | 说明 |
---|---|
Stream | 从列表开始列出满足条件的元素,遇到不满足的停止 |
Stream | 与 takeWhile() 相反,从第一个不满足条件的元素开始, 返回到列表结尾的元素 |
Stream | 创建一个可以为空的 Stream 流 |
Stream | 无限迭代, |
Stream | 迭代到指定条件后结束迭代 |
List<Integer> list = List.of(12, 22, 60, 34, 2, 123, 61, 32);
list.stream().takeWhile(x -> x < 60).forEach(System.out::println);// 12 22
list.stream().dropWhile(x -> x < 60).forEach(System.out::println);// 60 34 2 123 61 32
Stream.ofNullable(null).count();
Stream.iterate(0, x -> x + 1).forEach(System.out::println);
Stream.iterate(0, x -> x < 100, x -> x + 1).forEach(System.out::println);
4. JDK 11 新特性
特性 | 说明 |
---|---|
局部变量类型推断 var | 局部变量类型使用 var 代替,jdk10 新增 |
集合新增 copyOf(Collection<? extends E> coll) | 创建只读集合,jdk10 新增 |
String 新增的方法:boolean isBlank() | 判断字符串是否非空 |
String 新增的方法:String strip() | 去除首尾空格:全角和半角空白字符 |
String 新增的方法:String stripTrailing() | 去除尾部空格:全角和半角空白字符 |
String 新增的方法:String stripLeading() | 去除首部空格:全角和半角空白字符 |
String 新增的方法:String repeat(int count) | 重复 count 边 |
String 新增的方法:long lines().count() | 行数统计 |
HttpClient | 更方便的发送Http请求 |
动态编译 | 不需要 javac 命令编译代码,可以直接使用 java 命令运行 .java 文件 |
4.1 局部类型推断 var
- var 不是关键字,变量名字可以是 var
- 使用 var 的局部变量,必须初始化
- 不能用 null 初始化局部变量
public void test(){
var list = new ArrayList<>();
var i = 0;
int var = 0;
}
4.2 copyOf(Collection<? extends E> coll)
- 如果入参本身是一个只读集合,那么返回的是原集合
- 如果入参本身不是一个只读集合,那么返回一个新的只读集合
var list1 = new ArrayList<>();
list1.add(1);
list1.add(2);
list1.add(3);
var copyList1 = List.copyOf(list1);
System.out.println(list1 == copyList1);//false
var list2 = List.of(1, 2, 3);
var copyList2 = List.copyOf(list2);
System.out.println(list2 == copyList2);//true
5. JDK 12 - 16 新特性
特性 | 说明 |
---|---|
switch语句块的升级 | Lambda表达式,可以有返回值,省略 break;jdk 12 中新增,jdk 13中优化,jdk 14中确定 |
文本块升级 | """ """ 表示一个文本段;jdk 13中新增, jdk 15 中确定 |
instanceof的模式匹配 | 类型后面直接写变量名,无需强转; jdk14 中新增,jdk 15 中确定 |
record | 实体类定义更加简单,申明之后将自动获得全参构造器、getter、setter、equals、hashCode、toString方法 jdk14 |
空指针异常信息 | 空指针异常信息更加详细;jdk14 |
封闭类 | sealed:指定该类是一个封闭类; permits:允许继承的子类 |
Nashorn JavaScript 脚本引擎 | 维护成本太大 |
jpackage 打包工具 | windows: msi和exe;linux:deb和rpm;mac:pkg和dmg;jdk14新增;jdk16确定 |
ZGC垃圾回收器 | 全并发,标记-复制算法;JDK 11 中集成进来的,在 JDK 15 中正式转正 |
5.1 switch
// JDk12 之前写法
switch (day) {
case MONDAY:
case FRIDAY:
case SUNDAY:
System.out.println(6);
break;
case TUESDAY:
System.out.println(7);
break;
case THURSDAY:
case SATURDAY:
System.out.println(8);
break;
case WEDNESDAY:
System.out.println(9);
break;
}
// JDK12 中写法
switch (day) {
case MONDAY, FRIDAY, SUNDAY -> System.out.println(6);
case TUESDAY -> System.out.println(7);
case THURSDAY, SATURDAY -> System.out.println(8);
case WEDNESDAY -> System.out.println(9);
}
// JDK13 优化:可以有返回值
String result = switch (k) {
case 1 -> "one";
case 2 -> "two";
default -> "many";
};
System.out.println(result);
5.2 文本块升级
// jdk13 之前
String html = "<html>\n" +
" <body>\n" +
" <p>Hello, world</p>\n" +
" </body>\n" +
"</html>\n";
// jdk13 中
String html = """
<html>
<body>
<p>Hello, world</p>
</body>
</html>
""";
// 等同于:String s = "line 1\nline 2\nline 3\n"
String s = """
line 1
line 2
line 3
"""
5.3 instanceof
Object obj = "hello 14";
// JDK14 之前版本
if (obj instanceof String) {
// 需要强制类型转换
String str = (String) obj;
System.out.println("str length:" + str.length());
} else {
System.out.println("obj not string.");
}
// Jdk14中
if (obj instanceof String str) {
System.out.println("str length:" + str.length());
} else {
System.out.println("obj not string.");
}
5.4 record
// 声明
public record User (String userId, String name){
}
// 测试
public static void main(String[] args) {
User user = new User("AAA", "1111111");
System.out.println(user);
}
5.5 NullPointerException
String str = null;
System.out.println(str.length());
/**
* JDK14 之前版本异常输出:
* Exception in thread "main" java.lang.NullPointerException at DemoNull.main(DemoNull.java:9)
*/
/**
* Jdk14 版本异常输出:
* Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.length()" because "str" is null
* at DemoNull.main(DemoNull.java:9)
*/
5.6 封闭类
// sealed:指定该类是一个封闭类;
// permits:指定该类只能被 ZanSan 继承
public sealed class Stu permits ZanSan {
}
// 子类必须是 final 修饰的
public final class ZanSan extends Stu{
}
评论区