侧边栏壁纸
博主头像
码森林博主等级

一起走进码森林,享受编程的乐趣,发现科技的魅力,创造智能的未来!

  • 累计撰写 146 篇文章
  • 累计创建 74 个标签
  • 累计收到 4 条评论

目 录CONTENT

文章目录

JDK 8~16 版本新特性

码森林
2022-01-01 / 0 评论 / 1 点赞 / 603 阅读 / 20,643 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2022-01-01,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

1. JDK 版本变更

每6个月发布一个小版本,每3年发布一个长期支持的版本。

版本发布日期延长日期长期支持
JDK 72011年9月2022年9月LTS
JDK 82014年3月2030年12月LTS
JDK 92017年9月
JDK 102018年3月
JDK 112018年9月2026年9月LTS
JDK 122019年3月
JDK 132019年9月
JDK 142020年3月
JDK 152020年9月
JDK 162021年3月
JDK 172021年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提供的函数式接口:
接口方法方法说明
SupplierT get()供应接口:不需要参数,按照某种实现逻辑(Lambda表达式)返回一个数据
Consumervoid accept(T t)对传入的参数进行操作,具体操作由 Lambda 表达式实现
Consumer andThen(Consumer after)返回一个组合的Consumer,依次执行此操作,然后执行after操作
Predicateboolean test(T t)对给定的参数进行判断,返回 true 或 false
Predicate negate()返回一个逻辑的否定,对应 逻辑非(!)
Predicate and(Predicate pre)返回一个组合判断,对应 短路与(&&)
Predicate or(Predicate pre)返回一个组合判断,对应 短路或(||)
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 filter(Predicate predicate)过滤流:筛选出符合Lambda表达式的元素中间操作
Stream limit(long maxSize)截断流:使其元素不超过给定对象中间操作
Stream skip(long n)跳过元素,返回一个扔掉了前n个元素的流,若流中元素不足n个,
则返回一个空流,与 limit 互补
中间操作
Stream distinct()通过流所生成元素的hashCode()和equals去除重复元素中间操作
Stream map(Function<T, R> mapper)将元素转换成其他形式或提取信息。接受一个函数作为参数,
该函数会被应用到每个元素上,并将其映射成一个新的元素
中间操作
Stream flatMap(Function<T,R> mapper)各个数组并不是分别映射成一个流,而是映射成流的内容。一对多中间操作
Stream sorted()自然排序中间操作
Stream sorted(Comparator comparator)定制排序中间操作
boolean allMatch(Predicate predicate)检查是否匹配所有元素终结操作
boolean anyMatch(Predicate predicate)检查是否至少匹配一个元素终结操作
boolean noneMatch(Predicate predicate)检查是否没有匹配所有元素终结操作
Optional findFirst()返回第一个元素终结操作
Optional findAny()返回当前流中的任意元素终结操作
long count()返回流中元素的总个数终结操作
Optional max(Comparator comparator)返回流中最大值终结操作
Optional min(Comparator comparator)返回流中最小值终结操作
T reduce(T identity, BinaryOperator accumulator)归约操作可以将流中元素反复结合起来,得到一个值终结操作
R collect(Collector<T, A, R> collector)收集:接收一个Collector接口实现,用于给Stream中汇总的方法终结操作
Object[] toArray()收集:返回一个数组终结操作
void forEach(Consumer action)遍历流中的元素终结操作
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 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 takeWhile(Predicate<? super T> predicate)从列表开始列出满足条件的元素,遇到不满足的停止
Stream dropWhile(Predicate<? super T> predicate)与 takeWhile() 相反,从第一个不满足条件的元素开始,
返回到列表结尾的元素
Stream ofNullable(T t)创建一个可以为空的 Stream 流
Stream iterate(final T seed, final UnaryOperator f)无限迭代,
Stream iterate(T seed, Predicate<? super T> hasNext, UnaryOperator next)迭代到指定条件后结束迭代
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{
}
1

评论区