JDK9-17新特性

发布时间 2023-12-13 21:37:19作者: yanggdgg

一、JDK9新特性

1. 概述

JDK9新特性主要包括:

  • 模块化系统
  • JShell
  • 只读集合工厂方法
  • 接口的私有方法
  • String存储结构改变
  • try...catch升级
  • 平台日志API和服务
  • 垃圾回收器

2. 模块化系统

没有使用模块化时存在的问题:

  导入依赖时,需要加载全部的包,影响系统性能。

 

 Java 运行环境的膨胀和臃肿。每次JVM启动的时候,至少会有30~60MB的内存加载,主要原因是JVM需要加载rt.jar,不管其中的类是否被classloader加载,第一步整个jar都会被JVM加载到内存当中去(而模块化可以根据模块的需要加载程序运行需要的class)

当代码库越来越大,创建复杂,盘根错节的“意大利面条式代码”的几率呈指数级的增长。不同版本的类库交叉依赖导致让人头疼的问题,这些都阻碍了 Java 开发和运行效率的提升。

 

 

很难真正地对代码进行封装, 而系统并没有对不同部分(也就是 JAR 文件)之间的依赖关系有个明确的概念。每一个公共类都可以被类路径之下任何其它的公共类所访问到,这样就会导致无意中使用了并不想被公开访问的 API。

 

本质上讲也就是说,用模块来管理各个package,通过声明某个package暴露,模块(module)的概念,其实就是package外再裹一层,不声明默认就是隐藏。因此,模块化使得代码组织上更安全,因为它可以指定哪些部分可以暴露,哪些部分隐藏。

使用模块化系统,可以按需导入对应的模块。

实现目标

  • 模块化的主要目的在于减少内存的开销

  • 只须必要模块,而非全部jdk模块,可简化各种类库和大型应用的开发和维护

  • 改进 Java SE 平台,使其可以适应不同大小的计算设备

  • 改进其安全性,可维护性,提高性能

使用

在src下创建module-info.java

导出:

module xxx{
    exports 包名/模块;
}

导入:

module xxx{
    requires 包名/模块;
}

3. JShell命令

像Python 和 Scala 之类的语言早就有交互式编程环境 REPL (read - evaluate - print - loop)了,以交互式的方式对语句和表达式进行求值。开发者只需要输入一些代码,就可以在编译前获得对程序的反馈。而之前的Java版本要想执行代码,必须创建文件、声明类、提供测试方法方可实现。

4. 接口的私有方法

public interface JDK9Demo4 {
    //接口默认都是共有静态常量
    public static final int NUM = 10;
    //接口默认方法都是抽象方法
    public abstract void method1();

    //jdk8接口可以有静态方法
    public static void method2(){
        System.out.println("接口中的静态方法");
    }
    //jdk8接口可以有默认方法
    public default void method3(){
        System.out.println("接口中的默认方法");
    }
    //jdk9接口可以有私有方法
    private void method4(){
        System.out.println("接口中的私有方法");
    }
}

5. 钻石操作符升级

Comparator<Object> com = new Comparator<>() {
    @Override
    public int compare(Object o1, Object o2) {
        return 0;
    }
};

在jdk8中 上面的代码会报错:<>必须写明类型,jdk9对<>做了升级,不写也不报错。

6. try...catch

在jdk8中,可以自动关闭资源,但必须在try子句中初始化。

jdk9可以在外部初始化,try子句写对象名,也可以自动关闭。

try(inputStream;outputStream){

}

7. String存储结构变更

不再使用char[]存储,改为byte[],节省空间。

同样的,StringBuffer 和 StringBuilder也做了存储结构的变更。

8. of() 创建只读集合

jdk9引入了of()方法,可以方便的创建只读集合

public class Demo5 {
    public static final List<String> list = List.of("type_weixin","type_zhifubao","type_yinhangka");
    public static void main(String[] args) {
        //jdk8以前创建只读集合
        List<String> list = new ArrayList<>();
        list.add("张三");
        list.add("李四");
        list.add("王五");
        Collection<String> list2 = Collections.unmodifiableCollection(list);
//        list2.add("赵六");
//        System.out.println(list2);
        //jdk9创建只读集合
        List<String> list3 = List.of("张三", "李四", "王五");
        System.out.println(list3);
        Set<String> set1 = Set.of("AA", "BB", "CC");
        System.out.println(set1);
        Map<String, String> map = Map.of("k1", "v1", "k2", "v2");
        System.out.println(map);
    }
}

9. InputStream增强

使用 transferTo 可以将输入流中的内容直接交给输出流输出

10. stream API增强

  • 在 Java 9 中,Stream API 变得更好,Stream 接口中添加了 4 个新的方法:takeWhile, dropWhile, ofNullable,还有个 iterate 方法的新重载方法,可以让你提供一个 Predicate (判断条件)来指定什么时候结束迭代。

  • 除了对 Stream 本身的扩展,Optional 和 Stream 之间的结合也得到了改进。现在可以通过 Optional 的新方法 stream() 将一个 Optional 对象转换为一个(可能是空的) Stream 对象。

10.1 takeWhile() 

用于从 Stream 中获取一部分数据,接收一个 Predicate 来进行选择。在有序的Stream 中,takeWhile 返回从开头开始的尽量多的元素

 

public static void main(String[] args) throws Exception {
    List<Integer> list = Arrays.asList(10,20,30,40,30,20,10);
    list.stream().takeWhile(t->t<40).forEach(System.out::println);

    List<Integer> list2 = Arrays.asList(1,2,3,4,5,6,7);
    list2.stream().takeWhile(t->t<7).forEach(System.out::println);
}

10.2 dropWhile()

dropWhile 的行为与 takeWhile 相反,返回剩余的元素

10.3 ofNullable()

Java 8 中 Stream 不能完全为null,否则会报空指针异常。

而 Java 9 中的 ofNullable 方法允许我们创建一个单元素 Stream,可以包含一个非空元素,也可以创建一个空Stream。

public static void main(String[] args) throws Exception {
    //允许通过
    Stream<String> streams = Stream.of("AA","BB",null);
    System.out.println(streams.count());
    //不允许通过
    /*Stream<Object> stream2 = Stream.of(null);
        System.out.println(stream2.count());*/
    //允许通过
    Stream<Object> stream2 = Stream.ofNullable(null);
    System.out.println(stream2.count());
}

10.4 iterate() 重载

这个 iterate 方法的新重载方法,可以让你提供一个 Predicate (判断条件)来指定什么时候结束迭代。

public static void main(String[] args) throws Exception {
    //原始方式
    Stream.iterate(1,i->i+1).limit(50).forEach(System.out::println);
    //增强方式
    Stream.iterate(1,i->i<60,i->i+1).forEach(System.out::println);
}

10.5 Optional类中stream()使用

public static void main(String[] args) throws Exception {
    List<String> list = new ArrayList<>();
    list.add("张三");
    list.add("李四");
    list.add("王五");
    list.add("赵六");
    Optional<List<String>> optional = Optional.ofNullable(list);
    optional.stream().flatMap(x->x.stream()).limit(2).forEach(System.out::println);

    list.stream().limit(2).forEach(System.out::println);
}

Optional接收一个集合对象,底层也是用集合容器进行存储,所以使用一次流进行遍历得到的是集合对象,

调用flatMap方法 能获取到集合中的元素

二、JDK10新特性

1. 局部变量类型推断

使用var声明变量,var进行类型推断

public class Demo1 {
    public static void main(String[] args) {
        method1();
    }
    public static void method1(){
        var i = 1;
        var list = new ArrayList<String>();
        list.add("张三");
        list.add("李四");
        System.out.println(list);
    }
}

var不是一个关键字。

var没有改变java是强语言的本质,只是进行类型的推断。

以下情况不能推断:

public static void method2(){
        //由于值是null,类型无法推断
//        var i = null;
        //方法引用无法使用var
        //var a = System.out::println;
        //lamdba无法推断var的类型,故不能用
       /* Function<Integer,String> fun = (Integer a)->{
            return a+"";
        };*/
       /* var fun2 = (Integer a)->{
            return a+"";
        };*/
        //静态数组后面不加类型的时候推断不出来var的类型,故不能用
        int[] arr = new int[]{1,2,3};
        int[] arr2 = {1,2,3};
        //var arr3 = {1,3,4};
    }

2. 新增只读集合方法 copyOf()

public static void main(String[] args) throws Exception {
    //示例1
    var list1 = List.of("AA","BB","CC");
    var list2 = List.copyOf(list1);
    System.out.println(list1==list2);//true
    //示例2
    var list3 = new ArrayList<String>();
    list3.add("AA");
    list3.add("BB");
    List<String> list4 = List.copyOf(list3);
    System.out.println(list3==list4); //false
}

copyOf 方 法 会 先 判 断 来 源 集 合 是 不 是AbstractImmutableList 类型的,如果是,就直接返回,如果不是,则调用 of 创建一个新的集合。

示例2因为用的 new 创建的集合,不属于不可变 AbstractImmutableList 类的子类,所以 copyOf 方法又创建了一个新的实例,所以为false。

注意:使用of和copyOf创建的集合为不可变集合,不能进行添加、删除、替换、排序等操作,不然会报 java.lang.UnsupportedOperationException 异常。

三、JDK11新特性

1. ZGC垃圾回收器

GC是java主要优势之一。 然而, 当GC停顿太长, 就会开始影响应用的响应时间。消除或者减少GC停顿时长, java将对更广泛的应用场景是一个更有吸引力的平台。此外, 现代系统中可用内存不断增长,用户和程序员希望JVM能够以高效的方式充分利用这些内存, 并且无需长时间的GC暂停时间。

  • ZGC, A Scalable Low-Latency Garbage Collector(Experimental)ZGC, 这应该是JDK11最为瞩目的特性, 没有之一。 但是后面带了Experimental,说明这还不建议用到生产环境。

  • ZGC是一个并发, 基于region, 压缩型的垃圾收集器, 只有root扫描阶段会STW(stop the world), 因此GC停顿时间不会随着堆的增长和存活对象的增长而变长。

优势:

  • GC暂停时间不会超过10ms

  • 既能处理几百兆的小堆, 也能处理几个T的大堆(OMG)

  • 和G1相比, 应用吞吐能力不会下降超过15%

  • 为未来的GC功能和利用colord指针以及Load barriers优化奠定基础

  • 初始只支持64位系统

2. Optional加强

3. 新增HTTP客户端API

  • HTTP,用于传输网页的协议,早在1997年就被采用在目前的1.1版本中。直到2015年,HTTP2才成为标准。

  • HTTP/1.1和HTTP/2的主要区别是如何在客户端和服务器之间构建和传输数据。HTTP/1.1依赖于请求/响应周期。 HTTP/2允许服务器“push”数据:它可以发送比客户端请求更多的数据。这使得它可以优先处理并发送对于首先加载网页至关重要的数据。

  • 这是 Java 9 开始引入的一个处理 HTTP 请求的的 HTTP Client API,该API 支持同步和异步,而在 Java 11 中已经为正式可用状态,你可以在java.net 包中找到这个 API。

  • 它 将 替 代 仅 适 用 于 blocking 模 式 的 HttpURLConnection(HttpURLConnection是在HTTP 1.0的时代创建的,并使用了协议无关的方法),并提供对WebSocket 和 HTTP/2的支持

public static void main(String[] args) throws Exception {
    HttpClient client = HttpClient.newHttpClient();
    HttpRequest request =
        HttpRequest.newBuilder(URI.create("http://www.baidu.com")).build();
    HttpResponse.BodyHandler<String> responseBodyHandler = HttpResponse.BodyHandlers.ofString();
    HttpResponse<String> response = client.send(request, responseBodyHandler);
    String body = response.body();
    System.out.println(body);
}

四、JDK14新特性

在NullPointerException时,指明为null的变量

之前:

 jdk14:

五、JDK15新特性

新增 text blocks 文本块,解决多行字符串换行问题

public static void main(String[] args) {
    //jdk15增加文本块
    String str = """
        hello
        world
        hahaha
        """;
}

六、JDK16新特性

在Java16中正式发布Switch升级,其目的是为了解决switch语句的一些不规则性成为障碍

  • 比如case标签之间的默认控制行为

  • case块中的默认范围

  • 无意义的break语句。

public static void main(String[] args) {
    int level = new Random().nextInt(4);
    String strLevel;
    switch (level){
        case 1 -> strLevel="优秀";
        case 2 -> strLevel="良好";
        default -> strLevel="一般";
    }
    System.out.println(strLevel);
}

还可以写成:

public static void main(String[] args) {
    int level = new Random().nextInt(4);
    String strLevel = switch (level){
        case 1-> "优秀";
        case 2-> "良好";
        default -> "一般";
    };
    System.out.println(strLevel);
}

根据月份判断季节:

public static void main(String[] args) {
    int level = new Random().nextInt(12);
    String jiJi = null;
    switch (level){
        case 3,4,5 -> jiJi =  "春天";
        case 6,7,8 -> jiJi =  "夏天";
        case 9,10,11 -> jiJi =  "秋天";
        case 12,1,2 -> jiJi = "冬天";
    };
    System.out.println(jiJi);
}

还有:

public static void main(String[] args) {
    int level = new Random().nextInt(4);
    String strLeave = switch (level){
        case 1 -> {
            System.out.println("优秀");
            yield "优秀";
        }
        default -> "进步空间很大";
    };
    System.out.println(strLeave);
}

虽然写法更简单了,但是编译后的代码还是原本的格式,只是做了翻译。