Java9/Java10/Java11的新特性

1 - JDK 9 的发布 

1 经过4次跳票,历经曲折的Java 9 终于终于在2017年9月21日发布。

2 从Java 9 这个版本开始,Java 的计划发布周期是 6 个月,下一个 Java 的主版 本将于 2018 年 3 月发布,命名为 Java 18.3,紧接着再过六个月将发布 Java 18.9。

3 这意味着Java的更新从传统的以特性驱动的发布周期,转变为以时间驱动的 (6 个月为周期)发布模式,并逐步的将 Oracle JDK 原商业特性进行开源。

4 针对企业客户的需求,Oracle 将以三年为周期发布长期支持版本(long term support)。

5 Java 9 提供了超过150项新功能特性,包括备受期待的模块化系统、可交互 的 REPL 工具:jshell,JDK 编译工具,Java 公共 API 和私有代码,以及安 全增强、扩展提升、性能管理改善等。可以说Java 9是一个庞大的系统工程, 完全做了一个整体改变。

2 - JDK 9 主要新特性一览

1 模块化系统(重点)

2 jShell命令(重点)

3 多版本兼容jar包

4 接口的私有方法

5 钻石操作符的使用升级

6 语法改进:try语句

7 String存储结构变更

8 便利的集合特性:of()

9 增强的Stream API

10 全新的HTTP客户端API

11 Deprecated的相关API

12 javadoc的HTML 5支持

13 Javascript引擎升级:Nashorn

14 java的动态编译器

 

✔ 官方提供的新特性列表: https://docs.oracle.com/javase/9/whatsnew/toc.htm#JSNEW-GUIDC23AFD78-C777-460B-8ACE-58BE5EA681F6

✔ 或参考 Open JDK http://openjdk.java.net/projects/jdk9/

✔ 在线Oracle JDK 9 Documentation https://docs.oracle.com/javase/9/

3 - JDK9模块化系统: Jigsaw → Modularity

 谈到 Java 9 大家往往第一个想到的就是 Jigsaw 项目。众所周知,Java 已经 发展超过 20 年(95 年最初发布),Java 和相关生态在不断丰富的同时也越 来越暴露出一些问题:

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

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

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

 

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

✔ 实现目标

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

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

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

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

✔ 模块将由通常的类和新的模块声明文件(module-info.java)组成。

该文件是位于 java代码结构的顶层,该模块描述符明确地定义了我们的模块需要什么依赖关系, 以及哪些模块被外部使用。在exports子句中未提及的所有包默认情况下将封装在 模块中,不能在外部使用

✔ 实现步骤

  ① 如果我们想要在模块Day13下调用模块Java9Test下的结构,就需要在模块 Day13 和 模块 Java9Test下的src文件下有以下操作

 

   ② 在Day13 的module-info.java中声明:

module Day13 {
    requires Java9test;
    requires junit;
    //requires 指明对其它模块的依赖。
}

  ③在Java9Test 的module-info.java中声明:

module Java9test {
    exports com.lzh.bean;
    // exports 控制着哪些包可以被其它模块访问到。所有不被导出的包默认 都被封装在模块里面。(通俗的讲就是对外暴露其结构)
}

4 - JDK9的REPL工具: jShell命令

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

2 设计理念:即写即得、快速运行

3 实现目标

4 Java 9 中终于拥有了 REPL工具:jShell。让Java可以像脚本语言一样运行,从 控制台启动jShell,利用jShell在没有创建类的情况下直接声明变量,计算表达式, 执行语句。即开发时可以在命令行里直接运行Java的代码,而无需创建Java文 件,无需跟人解释”public static void main(String[] args)”这句废话

  ① jShell也可以从文件中加载语句或者将语句保存到文件中。

  ② jShell也可以是tab键进行自动补全和自动添加分号。

5 jShell

  调出 jShell

   获取帮助 /help

   基本使用(可自由写Java代码)

Tips:在 JShell 环境下,语句末尾的“;” 是可 选的。但推荐还是最好加上。提高代码可读 性。

Tips:我们还可以重新定义相同方法名和参 数列表的方法,即为对现有方法的修改(或 覆盖)。

这里还有许多用法,就不介绍了

5 - JDK9语法改进:接口的私有方法

1 Java 8中规定接口中的方法除了抽象方法之外,还可以定义静态方法 和默认的方法。一定程度上,扩展了接口的功能,此时的接口更像是 一个抽象类。

2 在Java 9中,接口更加的灵活和强大,连方法的访问权限修饰符都可 以声明为private的了,此时方法将不会成为你对外暴露的API的一部分

package com.lzh.java;

public interface MyInterface {
    //如下的三个方法的权限修饰符都是public
    void methodAbstract();

    static void methodStatic(){
        System.out.println("我是接口中的静态方法");
    }

    default void methodDefault(){
        System.out.println("我是接口中的默认方法");

        methodPrivate();
    }
    //jdk 9中允许接口中定义私有的方法
    private void methodPrivate(){
        System.out.println("我是接口中的私有方法");
    }
}
MyInterface.java
package com.lzh.java;

public class MyInterfaceImpl implements MyInterface {
    
    @Override
    public void methodAbstract() {

    }

//    @Override
    public void methodDefault() {
        System.out.println("实现类重写了接口中的默认方法");
    }

    public static void main(String[] args) {
        //接口中的静态方法只能由接口自己调用
        MyInterface.methodStatic();
        //接口的实现类不能调用接口的静态方法
//        MyInterfaceImpl.methodStatic();

        MyInterfaceImpl impl = new MyInterfaceImpl();
        impl.methodDefault();
        //接口的私有方法,不能在接口外部调用
//        impl.methodPrivate();
    }
}
MyInterfaceImpl.java

6 - JDK9语法改进:钻石操作符使用升级

1 我们将能够与匿名实现类共同使用钻石操作符(diamond operator)Java 8 中如下的操作是会报错的:

Comparator<Object> comparator = new Comparator<>() {
    @Override
    public int compare(Object o1, Object o2) {
        return 0;
    }
};
// 编译报错信息:Cannot use “<>” with anonymous inner classes

2 Java 9中如下操作可以正常执行通过:

 //java9特性五:钻石操作符的升级
    @Test
    public void test2() {
        //钻石操作符与匿名内部类在java 8中不能共存。在java9可以。
        Comparator<Object> com = new Comparator<>() {
            @Override
            public int compare(Object o1, Object o2) {
                return 0;
            }
        };

        //jdk7中的新特性:类型推断
        ArrayList<String> list = new ArrayList<>();

    }

7 - JDK9语法改进:try语句

1 Java 8 中,可以实现资源的自动关闭,但是要求执行后必须关闭的所有资源必 须在try子句中初始化,否则编译不通过。

try(InputStreamReader reader = new InputStreamReader(System.in)){
     //读取数据细节省略 
}catch (IOException e){ 
    e.printStackTrace(); 
}

2 Java 9 中,用资源语句编写try将更容易,我们可以在try子句中使用已经初始 化过的资源,此时的资源是final的:

InputStreamReader reader = new InputStreamReader(System.in); 

OutputStreamWriter writer = new OutputStreamWriter(System.out); 

try (reader; writer) { 
    //reader是final的,不可再被赋值 
    //reader = null; 
    //具体读写操作省略 
} catch (IOException e) { 
    e.printStackTrace(); 
}
    //java9 特性六:try操作的升级
    public static void main(String[] args) {
        //java 8之前的资源关闭的操作
//        InputStreamReader reader = null;
//        try {
//            reader = new InputStreamReader(System.in);
//            char[] cbuf = new char[20];
//            int len;
//            if((len = reader.read(cbuf) )!= -1){
//                String str = new String(cbuf,0,len);
//                System.out.println(str);
//            }
//        } catch (IOException e) {
//            e.printStackTrace();
//        } finally {
//            if(reader != null){
//                try {
//                    reader.close();
//                } catch (IOException e) {
//                    e.printStackTrace();
//                }
//
//            }
//        }

        //java 8中资源关闭操作: Java 8 中,可以实现资源的自动关闭
        //要求自动关闭的资源的实例化必须放在try的一对小括号中
//        try(InputStreamReader reader = new InputStreamReader(System.in)){
//            char[] cbuf = new char[20];
//            int len;
//            if((len = reader.read(cbuf) )!= -1){
//                String str = new String(cbuf,0,len);
//                System.out.println(str);
//            }
//        } catch (IOException e) {
//            e.printStackTrace();
//        }

        //java9中资源关闭操作:需要自动关闭的资源的实例化可以放在try的一对小括号外。
        //此时的资源属性是常量,声明为final的,不可修改
        InputStreamReader reader = new InputStreamReader(System.in);
        try (reader) {

            char[] cbuf = new char[20];
            int len;
            if((len = reader.read(cbuf) )!= -1){
                String str = new String(cbuf,0,len);
                System.out.println(str);
            }

//            reader = null;
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
代码举例

8 - JDK9 String存储结构变更

1 在以前的版本中,String一直是用char[]存储,但是在Java9中,String 再也不用 char[] 来存储啦,改成了 byte[] 加上编码标记,节约 了一些空间

public final class String 
    implements java.io.Serializable, Comparable<String>, CharSequence {
    @Stable 
    private final byte[] value;
}

2 那StringBufferStringBuilder 是否仍无动于衷呢?

AbstractStringBuilder StringBuilder, StringBuffer将更新使用相同的表示,将HotSpot VM的内在(固有的,内置的)字符串操作

9 - JDK9 集合工厂方法:快速创建只读集合

1 要创建一个只读、不可改变的集合,必须构造和分配它,然后添加元素,最后 包装成一个不可修改的集合。

//java8中的写法:
    @Test
    public void test1() {
        List<String> namesList = new ArrayList<>();
        namesList.add("Joe");
        namesList.add("Bob");
        namesList.add("Bill");
        //返回的namesList是一个只读的集合
        namesList = Collections.unmodifiableList(namesList);
        namesList.add("Tom");

        System.out.println(namesList);

    }

// 缺点:我们一下写了五行。即:它不能表达为单个表达式。

2 Java9 快速创建 只读集合 代码举例

    @Test
    public void test2() {
        List<String> list = Collections.unmodifiableList(Arrays.asList("a", "b", "c"));
        Set<String> set = Collections.unmodifiableSet(new HashSet<>(Arrays.asList("a", "b", "c")));
        // 如下操作不适用于jdk 8 及之前版本,适用于jdk 9
        Map<String, Integer> map = Collections.unmodifiableMap(new HashMap<>() {
            {
                put("a", 1);
                put("b", 2);
                put("c", 3);
            }
        });
        map.forEach((k, v) -> System.out.println(k + ":" + v));

    }

    @Test
    public void test3() {
        //此时得到的集合list也是一个只读集合。
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
        //报异常
        list.add(6);

    }

    //java9新特性八:集合工厂方法:创建只读集合
    @Test
    public void test4() {
        List<Integer> list1 = List.of(1, 2, 3, 4, 5);
        //不能添加
//        list1.add(6);
        System.out.println(list1);

        Set<Integer> set1 = Set.of(23, 3, 54, 65, 43, 76, 87, 34, 46);
        //不能添加
//        set1.add(4);
        System.out.println(set1);

        Map<String, Integer> map1 = Map.of("Tom", 23, "Jerry", 54, "HanMeimei", 12);
        //不能添加
        //map1.put("Lilei",34);

        System.out.println(map1);

        Map<String, Integer> map2 = Map.ofEntries(Map.entry("Tom", 34), Map.entry("Jerry", 21));
//        map2.put("Lilei",34);
        System.out.println(map2);


    }
View Code

10 - JDK9 InputStream的新方法:tranferTo()

1 InputStream 终于有了一个非常有用的方法:transferTo,可以用来将数据直接 传输到 OutputStream,这是在处理原始数据流时非常常见的一种用法,如下示例

 //java9新特性九:InputStream的新方法:tranferTo()
    @Test
    public void test5() {
        ClassLoader cl = this.getClass().getClassLoader();
        try (InputStream is = cl.getResourceAsStream("hello.txt");
             OutputStream os = new FileOutputStream("src\\hello1.txt")) {
            is.transferTo(os); // 把输入流中的所有数据直接自动地复制到输出流中
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

11 - JDK9 Stream API的加强

1 Java 的 Steam API 是java标准库最好的改进之一,让开发者能够快速运算, 从而能够有效的利用数据并行计算。Java 8 提供的 Steam 能够利用多核架构 实现声明式的数据处理。

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

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

 

takeWhile()的使用

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

List<Integer> list = Arrays.asList(45, 43, 76, 87, 42, 77, 90, 73, 67, 88);
list.stream().takeWhile(x->x< 50).forEach(System.out::println);
//takeWhile 返回从开头开始的按照指定规则尽量多的元素
        
System.out.println();

list=Arrays.asList(1,2,3,4,5,6,7,8);
list.stream().takeWhile(x->x< 5).forEach(System.out::println);

 

5 dropWhile()的使用

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

 List<Integer> list = Arrays.asList(23, 43, 45, 55, 61, 54, 32, 2, 45, 89, 7);

//dropWhile():与 takeWhile 相反,返回剩余的元素。
list.stream().dropWhile(x -> x < 60).forEach(System.out::println);

 

6 ofNullable()的使用

Java 8 中 Stream 不能完全为null,否则会报空指针异常。而 Java 9 中的 ofNullable 方 法允许我们创建一个单元素 Stream,可以包含一个非空元素,也可以创建一个空 Stream。 

    @Test
    public void test2(){
        //of()参数中的多个元素,可以包含null值
        Stream<Integer> stream1 = Stream.of(1, 2, 3,null);
        stream1.forEach(System.out::println);
        //of()参数不能存储单个null值。否则,报异常
//        Stream<Object> stream2 = Stream.of(null);
//        stream2.forEach(System.out::println);
        Integer i = 10;
        i = null;
        //ofNullable():形参变量是可以为null值的单个元素
        Stream<Integer> stream3 = Stream.ofNullable(i);
        long count = stream3.count();
        System.out.println(count);
    }

 

7 iterate()重载的使用

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

// 原来的控制终止方式: 
Stream.iterate(1, i -> i + 1).limit(10).forEach(System.out::println);

// 现在的终止方式: 
Stream.iterate(1, i -> i < 100, i -> i + 1).forEach(System.out::println);

 

8 Optional获取Stream的方法

Optional类中stream()的使用

List<String> list = new ArrayList<>(); 
list.add("Tom"); 
list.add("Jerry"); 
list.add("Tim");

Optional<List<String>> optional = Optional.ofNullable(list); 

Stream<List<String>> stream = optional.stream(); 

stream.flatMap(x -> x.stream()).forEach(System.out::println);

12 - JDK9 Javascript引擎升级:Nashorn

1 Nashorn 项目在 JDK 9 中得到改进,它为 Java 提供轻量级的 Javascript 运行时。 Nashorn 项目跟随 Netscape 的 Rhino 项目,目的是为了在 Java 中实现一个高 性能但轻量级的 Javascript 运行时。Nashorn 项目使得 Java 应用能够嵌入 Javascript。它在 JDK 8 中为 Java 提供一个 Javascript 引擎。

2 JDK 9 包含一个用来解析 Nashorn 的 ECMAScript 语法树的 API。这个 API 使得 IDE 和服务端框架不需要依赖 Nashorn 项目的内部实现类,就能够分析 ECMAScript 代码。

13 - JDK 10 新特性之 局部变量类型推断

1 2018年3月21日,Oracle官方宣布Java10正式发布。

2 需要注意的是 Java 9 和 Java 10 都不是 LTS (Long-Term-Support) 版本。和 过去的 Java 大版本升级不同,这两个只有半年左右的开发和维护期。而未 来的 Java 11,也就是 18.9 LTS,才是 Java 8 之后第一个 LTS 版本。

3 JDK10一共定义了109个新特性,其中包含12个JEP(对于程序员来讲,真 正的新特性其实就一个[局部变量类型推断]),还有一些新API和JVM规范以及JAVA语言规范上 的改动。

4 JDK10的12个JEP(JDK Enhancement Proposal特性加强提议)参阅官方 文档:http://openjdk.java.net/projects/jdk/10/

 

局部变量类型推断:

1 产生背景

开发者经常抱怨Java中引用代码的程度。局部变量的显示类型声明,常常被认为 是不必须的,给一个好听的名字经常可以很清楚的表达出下面应该怎样继续。

2 好处

减少了啰嗦和形式的代码,避免了信息冗余,而且对齐了变量名,更容易阅读!

3 举例如下

  ✔ 场景一:类实例化时

  作为 Java开发者,在声明一个变量时,我们总是习惯了敲打两次变量类型,第 一次用于声明变量类型,第二次用于构造器。

  LinkedHashSet<Integer> set = new LinkedHashSet<>();

  ✔ 场景二:返回值类型含复杂泛型结构 变量的声明类型书写复杂且较长,尤其是加上泛型的使用

  Iterator<Map.Entry<Integer, Student>> iterator = set.iterator();

  ✔ 场景三: 我们也经常声明一种变量,它只会被使用一次,而且是用在下一行代码中, 比如

  URL url = new URL("http://www.atguigu.com");

  URLConnection connection = url.openConnection();

  Reader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));

4 尽管 IDE可以帮我们自动完成这些代码,但当变量总是跳来跳去的时候,可读 性还是会受到影响,因为变量类型的名称由各种不同长度的字符组成。而且, 有时候开发人员会尽力避免声明中间变量,因为太多的类型声明只会分散注意 力,不会带来额外的好处

类型推断详情,如下代码

    /*
    java10新特性一:局部变量的类型推断

     */
    @Test
    public void test1() {
        //1.声明变量时,根据所附的值,推断变量的类型
        var num = 10;

        var list = new ArrayList<Integer>();
        list.add(123);

        //2.遍历操作
        for (var i : list) {
            System.out.println(i);
            System.out.println(i.getClass());
        }

        //3.普通的遍历操作
        for (var i = 0; i < 100; i++) {
            System.out.println(i);
        }

    }

    @Test
    public void test2() {
        //1.局部变量不赋值,就不能实现类型推断
//        var num ;

        //2.lambda表示式中,左边的函数式接口不能声明为var
//        Supplier<Double> sup = () -> Math.random();

//        var sup = () -> Math.random();

        //3.方法引用中,左边的函数式接口不能声明为var
//        Consumer<String> con = System.out::println;

//        var con = System.out::println;

        //4.数组的静态初始化中,注意如下的情况也不可以
        int[] arr = {1, 2, 3, 4};
//        var arr = {1,2,3,4};
    }

    @Test
    public void test3() {
//        情况1:没有初始化的局部变量声明
//        var s = null;


//        情况6:catch块
//        try{
//
//        }catch(var e){
//            e.printStackTrace();
//        }


    }
    //情况2:方法的返回类型
//    public var method1(){
//
////        return 0;
//    }
    // 情况3:方法的参数类型
//    public void method2(var num){
//
//    }

    //情况4:构造器的参数类型
//    public Java10Test(var i){
//
//    }

    //情况5:属性
//    var num;


    @Test
    public void test4() {
        try {
            var url = new URL("http://www.atguigu.com");
            var connection = url.openConnection();
            var reader = new BufferedReader(
                    new InputStreamReader(connection.getInputStream()));
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
适用情况与不适用情况代码举例

 

5 总结 

1 适用于以下情况

//1.局部变量的初始化 
var list = new ArrayList<>(); 

//2.增强for循环中的索引 
for(var v : list) { 
    System.out.println(v); 
} 

//3.传统for循环中
 for(var i = 0;i < 100;i++) { 
    System.out.println(i); 
}

 

2 不适用以下情况

// 1 初始值为null
var num = null;

// 2 Lambda表达式
var r = () -> Math.random();

// 3 方法引用
var str = System.out :: println;

// 4 为数组静态初始化
var array = {1,2,3,4,5,6};

// 可自行测试

 

3 不适用以下结构

/*
情况1:没有初始化的局部变量声明 

情况2:方法的返回类型 

情况3:方法的参数类型 

情况4:构造器的参数类型 

情况5:属性 

情况6:catch块
*/

 最后注意点

1处理 var时,编译器先是查看表达式右边部分,并根据右边变量值的类型进行推断(var 工作原理),作为左边变量的类型,然后将该类型写入字节码当中(字节码文件中数据类型还是会显示出来)。

2 var不是一个关键字 你不需要担心变量名或方法名会与 var发生冲突,因为 var实际上并不是一个关键字, 而是一个类型名,只有在编译器需要知道类型的地方才需要用到它。除此之外,它 就是一个普通合法的标识符。也就是说,除了不能用它作为类名,其他的都可以, 但极少人会用它作为类名。

3 这不是JavaScript 首先我要说明的是,var并不会改变Java是一门静态类型语言的事实。编译器负责推 断出类型,并把结果写入字节码文件,就好像是开发人员自己敲入类型一样。 下面是使用 IntelliJ(实际上是 Fernflower的反编译器)反编译器反编译出的代码

14 - JDK 10 新特性之 集合新增创建不可变集合的方法

1 Java 9 开始,Jdk 里面为集合(List / Set / Map)都添加了 of (jdk9新增)和 copyOf (jdk10新增)方法,它们两个都用来创建不可变的集合,来看下它们的 使用和区别如下:

    //java10的新特性二:集合中新增的copyOf(),用于创建一个只读的集合
    @Test
    public void test5(){
        //示例1:
        var list1 = List.of("Java", "Python", "C");
        var copy1 = List.copyOf(list1);
        System.out.println(list1 == copy1); // true

        //示例2:
        var list2 = new ArrayList<String>();
        list2.add("aaa");
        var copy2 = List.copyOf(list2);
        System.out.println(list2 == copy2); // false

        //示例1和2代码基本一致,为什么一个为true,一个为false?
        //结论:copyOf(Xxx coll):如果参数coll本身就是一个只读集合,则copyOf()返回值即为当前的coll
        //如果参数coll不是一个只读集合,则copyOf()返回一个新的集合,这个集合是只读的。
    }

2 从 源 码 分 析 , 可以看出 copyOf 方 法 会 先 判 断 来 源 集 合 是 不 是 AbstractImmutableList 类型的,如果是,就直接返回,如果不是,则调用 of 创 建一个新的集合。

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

4 注意:使用of和copyOf创建的集合为不可变集合,不能进行添加、删除、替换、 排序等操作,不然会报 java.lang.UnsupportedOperationException 异常。 上面演示了 List 的 of 和 copyOf 方法,Set 和 Map 接口都有

15 - JDK11 新特性

1 北京时间 2018年9 月 26 日, Oracle 官方宣布 Java 11 正式发 布。这是 Java 大版本周期变化 后的第一个长期支持版本,非 常值得关注。从官网即可下载, 最新发布的 Java11 将带来 ZGC、 Http Client 等重要特性,一共包 含 17 个 JEP(JDK Enhancement Proposals,JDK 增强提案)。

JDK 11 将是一个 企业不可忽视的版本。从时间节点来看,JDK 11 的发布正 好处在 JDK 8 免费更新到期的前夕,同时 JDK 9、10 也陆续成为“历史版 本”

3 对于企业来说,选择 11 将意味着长期的、可靠的、可预测的技术路线图。 其中免费的OpenJDK11 确定将得到 OpenJDK 社区的长期支持, LTS 版本将 是可以放心选择的版本。

4 从 JVM GC 的角度,JDK11 引入了两种新的 GC,其中包括也许是划时代意义 的 ZGC,虽然其目前还是实验特性,但是从能力上来看,这是 JDK 的一个巨 大突破,为特定生产环境的苛刻需求提供了一个可能的选择。例如,对部 分企业核心存储等产品,如果能够保证不超过 10ms 的 GC 暂停,可靠性会 上一个大的台阶,这是过去我们进行 GC 调优几乎做不到的,是能与不能的 问题。

JDK 11 是一个长期支持版本(LTS, Long-Term-Support)

16 - JDK11 新增了一系列字符串处理方法

    //java 11新特性一:String中新增的方法
    @Test
    public void test1(){
//        isBlank():判断字符串是否为空白
        System.out.println("  \t  \t  \n  ".isBlank());
//        strip():去除首尾空白
        System.out.println("-----" + "  \t abc \t  \n  ".strip() + "-------");
        System.out.println("-----" + "  \t abc \t  \n  ".trim() + "-------");
//        stripTrailing():去除尾部空格
        System.out.println("-----" + "  \t abc \t  \n  ".stripTrailing() + "-------");
//        stripLeading():去除首部空格
        System.out.println("-----" + "  \t abc \t  \n  ".stripLeading() + "-------");
//        repeat(int count):复制字符串
        String str1 = "abc";
        String str2 = str1.repeat(5);
        System.out.println(str2);

//        lines().count():行数统计
        String str3 = "abc\ndef\ng";
        System.out.println(str3.lines().count());
    }
代码举例

17 - JDK11 Optional 加强

Optional 也增加了几个非常酷的方法,现在可以很方便的将一个 Optional 转换 成一个 Stream, 或者当一个空 Optional 时给它一个替代的

    //java11新特性二:Optional新增的方法
    @Test
    public void test2(){

        var op = Optional.empty();
        System.out.println(op.isPresent());//判断内部的value是否存在
        System.out.println(op.isEmpty());//判断内部的value是否为空

        op = Optional.of("abc");
        //orElseThrow():value非空,返回value;否则抛异常NoSuchElementException
        var obj = op.orElseThrow();
        System.out.println(obj);

        Optional<String> op1 = Optional.of("hello");
//        op = Optional.empty();
        //or:value非空,返回对应的Optional;value为空,返回形参封装的Optional
        Optional<Object> op2 = op.or(() -> op1);
        System.out.println(op2);//

    }
代码举例

18 - JDK11 局部变量类型推断升级

var上添加注解的语法格式,在jdk10中是不能实现的。在JDK11中加入了这样 的语法

    //java11新特性三:局部变量类型推断的升级
    @Test
    public void test3(){
        //错误的形式: 必须要有类型, 可以加上var
//        Consumer<String> con1 = (@Deprecated t) -> System.out.println(t.toUpperCase());
        // 正确的形式:
        // 使用var的好处是在使用lambda表达式时给参数加上注解。
         Consumer<String> con2 = (@Deprecated var t) -> System.out.println(t.toUpperCase());
    }

19 - JDK11 HttpClient替换原有的HttpURLConnection

  //java11新特性四:HttpClient替换原有的HttpURLConnection。
    @Test
    public void test4(){
        try {
            HttpClient client = HttpClient.newHttpClient();
            HttpRequest request = HttpRequest.newBuilder(URI.create("http://127.0.0.1:8080/test/")).build();
            HttpResponse.BodyHandler<String> responseBodyHandler = HttpResponse.BodyHandlers.ofString();
            HttpResponse<String> response = client.send(request, responseBodyHandler);
            String body = response.body();
            System.out.println(body);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    @Test
    public void test5(){
        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder(URI.create("http://127.0.0.1:8080/test/")).build();
        HttpResponse.BodyHandler<String> responseBodyHandler = HttpResponse.BodyHandlers.ofString();
        CompletableFuture<HttpResponse<String>> sendAsync = client.sendAsync(request, responseBodyHandler);
        sendAsync.thenApply(t -> t.body()).thenAccept(System.out::println);
        //HttpResponse<String> response = sendAsync.get();
        //String body = response.body();
        //System.out.println(body);


    }
全新的HTTP 客户端API

当然还有很多新特性,这里就不细说了

20 - 展望未来

1 随着云计算和 AI 等技术浪潮,当前的计算模式和场景正在发生翻天覆地的变 化,不仅对 Java 的发展速度提出了更高要求,也深刻影响着 Java 技术的发展 方向。传统的大型企业或互联网应用,正在被云端、容器化应用、模块化的微 服务甚至是函数(FaaS, Function-as-a-Service)所替代。

2 Java虽然标榜面向对象编程,却毫不顾忌的加入面向接口编程思想,又扯出匿 名对象之概念,每增加一个新的东西,对Java的根本所在的面向对象思想的一 次冲击。反观Python,抓住面向对象的本质,又能在函数编程思想方面游刃有 余。Java对标C/C++,以抛掉内存管理为卖点,却又陷入了JVM优化的噩梦。选 择比努力更重要,选择Java的人更需要对它有更清晰的认识。

3 Java 需要在新的计算场景下,改进开发效率。这话说的有点笼统,我谈一些自 己的体会,Java 代码虽然进行了一些类型推断等改进,更易用的集合 API 等, 但仍然给开发者留下了过于刻板、形式主义的印象,这是一个长期的改进方向。

 

posted @ 2020-06-30 08:03  赖正华  阅读(2337)  评论(0编辑  收藏  举报