Java11新特性 有更新!

  heardfate

    转载自CSDN

    简介

    Java11 是继 Java8 之后的第二个 LTS 版本。从 Java11 开始,Oracle JDK 将不再免费提供商业用途。可以在开发阶段使用它,但要在商业上使用它,则需要购买许可证。

    Java11 还排除了 JRE 或 Server JRE ,下载仅可以获得 JDK。Java11 功能还包括 Windows 和 MacOS 的更新打包格式的更改。Java11 中 Windows 的更新打包格式为 .zip,而不是 tar.gz。MacOS 的更新打包格式为 .dmg而不是 .app。

    Java 11的11个新功能
    1. Lambda 表达式的类型推断
    2. String 方法新增
    3. 简化启动单个源代码文件的方法
    4. 标准的HTTP客户端
    5. 默认的 toArray(IntFunction)
    6. Epsilon垃圾收集器
    7. 低开销堆分析
    8. 嵌套的访问控制
    9. ZGC(Z Garbage Collector)
    10. 编译器线程的动态分配
    11. 新文件方法
    Lambda 表达式的类型推断
    在 Lambda 表达式中使用局部变量类型推断是 Java11 引入的唯一与语言相关的特性。

    其实从 Java10 就已经开始引入局部变量类型推断的特性。类型推断允许使用关键字 var 作为局部变量的类型而不是实际类型,编译器根据分配给变量的值推断出类型。这一特性简化代码编写、节省开发者的工作时间,因为不用再需要显式声明局部变量的类型,而是可以使用关键字 var,从而不会使源代码过于复杂。

    可以使用关键字 var 声明局部变量,如下所示:

    var java11 = "Hello Java 11";
    System.out.println(java11);
    
    List<StudentAgeType<Age, Student>> types = /*...*/;
    

    在 Java 10之前需要这样过滤集合

    types.stream().filter(type -> check(type))
    

    Java10 中使用 @Nonnull ,来指明参数不可以返回 null,这个是一个静态的分析方法,运行时不报任何警告。

    types.stream().filter((@Nonnull StudentAgeType<Age, Student> type) -> check(type))
    

    Java11 中使用变量类型推断。

    types.stream().filter((@Nonnull var type) -> check(type))
    

    String 方法新增
    String::strip 此方法删除字符串开头和结尾的空格
    String::stripLeading 此方法删除字符串开头的空格
    String::stripTrailing 此方法删除字符串结尾的空格
    String::isBlank 此实例方法返回一个布尔值,空字符串和仅包含空格的字符串将被视为空白
    String::lines 此方法返回字符串流,该字符串流是按行分割的所有子字符串的集合
    String::repeat 此方法是简单的以 int 数字将字符串重复多次
    strip stripLeading stripTrailing
    和 String::trim 方法功能相似,这些方法从字符串的开头和结尾删除空格。

    String willStrip = "  Strip  ";
    System.out.println(willStrip.strip());
    String willStripLeading = " StripLeading  ";
    System.out.println(willStripLeading.stripLeading());
    String willStripTrailing = " StripTrailing  ";
    System.out.println(willStripTrailing.stripTrailing());
    

    输出如下:

    Strip
    StripLeading  
    StripTrailing
    

    isBlank
    String::isBlank 方法的功能是,使用 Character::isWhiteSpace 方法解析空格的定义相同的定义,如果字符串为空或仅包含空格,则返回 true。

    String blank = "   ";
    System.out.println(blank.isBlank());
    

    结果是 : true

    lines
    String::lines 方法的引入,使得处理多行字符串更容易。String::lines 方法将多行字符串值拆分为由行尾控制字符描绘的String对象流。通过将 String::split 方法与Java流结合可以实现相同的结果,但是 String::lines 方法更加方便和高效。

    var lineSeparator = System.lineSeparator();
    var author1 = "江户川柯南";
    var author2 = "阿秋莎";
    var author3 = "柯南道尔";
    var meet = author1 +lineSeparator + author2+lineSeparator +  author3;
    System.out.println(meet);
    System.out.println("==========");
    System.out.println(meet.lines().collect(Collectors.toList()));
    

    输出:

    江户川柯南
    阿秋莎
    柯南道尔
    ==========
    [江户川柯南, 阿秋莎, 柯南道尔]
    

    repeat
    此方法能够在创建标题或用于测试的场景中使用,该方法将整数x作为参数,并重复字符串 x多次。

    var repead = "repead";
    var tenRepead = repead.repeat(10);
    System.out.println(repead);
    System.out.println("========");
    System.out.println(tenRepead);
    

    输出:

    repead
    ========
    repeadrepeadrepeadrepeadrepeadrepeadrepeadrepeadrepeadrepead
    

    简化启动单个源代码文件的方法
    之前的 Java 文件启动方式:

    作为 * .class 文件
    作为 * .jar 文件中的主类
    作为模块中的主类
    Java11 中新增了一个启动方式,可以在源代码中声明类,启动时通过一条命令即可。

    之前启动一个源文件,需要经过编译和执行两步。

    $ javac HelloWorld.java
    $ java -cp . hello.World
    

    现在在Java11 中,只需要一步就可以完成。

    $ java HelloWorld.java
    

    标准的HTTP客户端

    Java11 对 Http Client API 进行了标准化,并且支持异步非阻塞。Http Client 的包名由 jdk.incubator.http 改为 java.net.http,该 API 通过 CompleteableFutures 提供非阻塞请求。

    Java11 中的新 Http Client API,既支持 HTTP/2 同时也向下兼容 HTTP/1.1。其 API 接口,与主流开源 API(如:Apache HttpClient、Jetty、OkHttp 等)类似甚至拥有更高的性能。

    接口名称 描述
    HttpClient.Builder HttpClient 构建工具类,构建者模式
    HttpRequest.BodyPublisher 将特定的 java 对象转换为可发送的 HTTP request body 字节流
    HttpRequest.Builder HTTP Request 构建者
    HttpResponse HTTP response
    HttpResponse.BodyHandler 处理接收到的 Response Body
    HttpResponse.BodySubscriber 将 Response Body 字节流转换为对应的 Java 对象
    HttpResponse.PushPromiseHandler PushPromise 处理器
    HttpResponse.ResponseInfo 初始化 response 信息 支持 BodyHandler 当 response 初始收到消息 在消息体处理之前
    WebSocket WebSocket Client
    WebSocket.Builder WebSocket Client 的构建者工具类
    WebSocket.Listener WebSocket 数据接收接口

    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.net.Authenticator;
    import java.net.InetSocketAddress;
    import java.net.ProxySelector;
    import java.net.URI;
    import java.net.http.HttpClient;
    import java.net.http.HttpRequest;
    import java.net.http.HttpResponse;
    import java.nio.file.Paths;
    import java.time.Duration;
    import java.util.concurrent.CompletableFuture;
    
    public class HttpClientDemo {
    
        public static void main(String[] args) throws IOException, InterruptedException {
    
            HttpClient client = HttpClient.newBuilder()
                    // 协议版本,HTTP/1.1 还是 HTTP/2
                    .version(HttpClient.Version.HTTP_2)
                    //是否支持重定向
                    .followRedirects(HttpClient.Redirect.NORMAL)
                    //配置代理
                    .proxy(ProxySelector.of(new InetSocketAddress("www-proxy.com", 8080)))
                    //认证
                    .authenticator(Authenticator.getDefault())
                    .build();
    
            // HttpRequest.Builder 构建 HttpRequest,可多次发送,不可更改
            HttpRequest request = HttpRequest.newBuilder()
                    // 请求rui
                    .uri(URI.create("http://www.feng.com/"))
                    // 超时时间
                    .timeout(Duration.ofMinutes(1))
                    // 请求头参数设置
                    .header("Content-Type", "application/json")
                    // POST 文件请求  GET,POST,PUT ... 等请求方式
                    .POST(HttpRequest.BodyPublishers.ofFile(Paths.get("file.json")))
                    .build();
            // 同步发送请求,阻塞,会抛出中断异常
            HttpResponse<String> response =
                    client.send(request, HttpResponse.BodyHandlers.ofString());
    
            // 异步发送
            CompletableFuture<String> future = client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
                    .thenApply(HttpResponse::body);
        }
    }
    

    更多实现请看:Java11 HttpClientUtil

    Epsilon垃圾收集器
    Epsilon 垃圾收集器是一个低开销垃圾回收器,因为它的工作机制是:控制内存分配,但是不执行任何实际的垃圾回收工作。

    使用:Epsilon 垃圾回收器和其他垃圾回收器一样,可以通过参数 -XX:+UseEpsilonGC 开启。

    下面是 no-op GC 的几个使用场景:

    性能测试:什么都不执行的 GC 非常适合用于 GC 的差异性分析。no-op (无操作)GC 可以用于过滤掉 GC 诱发的性能损耗,比如 GC 线程的调度,GC 屏障的消耗,GC 周期的不合适触发,内存位置变化等。此外有些延迟者不是由于 GC 引起的,比如 scheduling hiccups, compiler transition hiccups,所以去除 GC 引发的延迟有助于统计这些延迟。
    内存压力测试:在测试 Java 代码时,确定分配内存的阈值有助于设置内存压力常量值。这时 no-op 就很有用,它可以简单地接受一个分配的内存分配上限,当内存超限时就失败。例如:测试需要分配小于 1G 的内存,就使用-Xmx1g 参数来配置 no-op GC,然后当内存耗尽的时候就直接 crash。
    VM 接口测试:以 VM 开发视角,有一个简单的 GC 实现,有助于理解 VM-GC 的最小接口实现。它也用于证明 VM-GC 接口的健全性。
    极度短暂 job 任务:一个短声明周期的 job 任务可能会依赖快速退出来释放资源,这个时候接收 GC 周期来清理 heap 其实是在浪费时间,因为 heap 会在退出时清理。并且 GC 周期可能会占用一会时间,因为它依赖 heap 上的数据量。
    延迟改进:对那些极端延迟敏感的应用,开发者十分清楚内存占用,或者是几乎没有垃圾回收的应用,此时耗时较长的 GC 周期将会是一件坏事。
    吞吐改进:即便对那些无需内存分配的工作,选择一个 GC 意味着选择了一系列的 GC 屏障,所有的 OpenJDK GC 都是分代的,所以他们至少会有一个写屏障。避免这些屏障可以带来一点点的吞吐量提升。
    移除 Java EE 和 CORBA 模块
    下面这些包已被移除:

    java.xml.ws
    java.xml.bind
    java.activation
    java.xml.ws.annotation
    java.corba
    java.transaction
    java.se.ee
    jdk.xml.ws
    jdk.xml.bind
    嵌套的访问控制
    通过内部嵌套类的方法可以访问到主类中的私有方法,如下例:

    public class Main {
        private class MyPrivateClass {
            private void myPrivateMethod(){
                System.out.println("myPrivateMethod");
            }
        }
        public static void main(String[] args) throws NoSuchMethodException,InvocationTargetException, IllegalAccessException {
            Main m = new Main();
            MyPrivateClass mp = m.new MyPrivateClass();
            Method method = MyPrivateClass.class.getDeclaredMethod("myPrivateMethod");
            method.invoke(m);
        }
    }
    

    如果通过反射机制进行访问,将会得到一个 IllegalStateException。

    var ob = new RefluenceMainDemo();
    Method method = ob.getClass().getDeclaredMethod("myPrivate");
    method.invoke(ob);
    

    Jdk11 之前运行将会得到如下的错误输出:

    Exception in thread "main" java.lang.IllegalAccessException: Class com.feng.java11.demo.Main can not access a member of class com.feng.java11.demo.Main$MyPrivateClass with modifiers "private"
    	at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
    	at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:296)
    	at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:288)
    	at java.lang.reflect.Method.invoke(Method.java:491)
    	at com.feng.java11.demo.Main.main(Main.java:16)
    

    Java11 嵌套访问控制解决了这一问题,Java11 提供通过核心反射查询类文件属性的方法,java.lang.Class 包含以下三个新方法。
    java.lang.Class介绍三种方法反射 API 中:

    getNestHost() 返回此Class对象所属的嵌套的嵌套宿主
    getNestMembers() 确定给定的Class是否是此Class对象的嵌套对象
    isNestmateOf() 返回一个包含Class对象的数组,Class对象的数据表示此Class对象所属的嵌套的所有成员
    嵌套允许将嵌套的类编译为属于同一封闭类的不同类文件。然后允许它们访问彼此的私有类,而无需使用合成/桥接方法。

    ZGC(Z Garbage Collector)
    Z垃圾收集器(ZGC)是可伸缩的低延迟垃圾收集器。ZGC 可以同时执行所有耗时的工作,而不会将应用程序线程的执行停止超过10ms,因此 ZGC 适合于要求低延迟和/或使用非常大的堆(nTB)的应用程序。

    Z垃圾收集器可作为实验功能使用,并已通过命令行选项启用: -XX:+UnlockExperimentalVMOptions -XX:+UseZGC。

    编译器线程的动态分配
    Java11 可以使用新的命令行标志来动态控制编译器线程,命令行标志为: -XX:+ UseDynamicNumberOfCompilerThreads。VM 在具有分层编译模式的具有多个 CPU 的系统上启动大量编译器线程。使用此命令行标志不必担心编译请求或可用内存的数量。但是,这可能导致资源使用效率低下,因为空闲线程也可能消耗内存。因此,使用新的命令行标志,实现已更改。现在,每种类型只有一个编译器线程在启动时启动,然后对后续威胁的启动和关闭进行动态管理。

    新文件方法
    writeString() 在文件中写入某些内容
    readString() 读取文件中的内容
    isSameFile() 判断两个路径是否标识同一文件。
    writeString
    java.nio.file.Files 有两个重载的静态方法可将内容写入文件:

    public static Path writeString​(Path path, CharSequence csq, 
                                OpenOption... options) throws IOException
     
    public static Path writeString​(Path path, CharSequence csq, 
                                Charset cs, OpenOption... options) throws IOException
    
    public static void java11BeforeWriteDemo(){
    
        String str = "This is java11 before writer demo";
    
        try(BufferedWriter writer = new BufferedWriter(new FileWriter("src/main/resources/fileDemo.txt"));) {
            writer.write(str);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    public static void java11WriteDemo(){
        var File_Path = Paths.get("src/main/resources/fileDemo.txt");
    
        try {
    
            // 写入 This is write demo
            Files.writeString(File_Path, "This is java11 write demo", LinkOption.values());
    
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
    

    readString

    import java.io.File;
    import java.io.IOException;
    import java.nio.charset.StandardCharsets;
    import java.nio.file.Files;
    import java.nio.file.Path;
    import java.nio.file.Paths;
    
    public class FileDemo {
        public static void main(String[] args) {
            java11BeforeDemo();
            System.out.println("======");
            java11Demo();
        }
    
        public static void java11BeforeDemo(){
            // 1. 创建 Path
            Path path = Paths.get("src/main/resources/fileDemo.txt");
            //2. 读取数据
            try {
                byte[] bs = Files.readAllBytes(path);
                System.out.println(new String(bs));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        public static void java11Demo(){
            // 1. 创建 Path
            Path path = Paths.get("src/main/resources/fileDemo.txt");
            //2. 读取数据
            try {
                String string = Files.readString(path);
                System.out.println(string);
                String string1 = Files.readString(path,StandardCharsets.UTF_8);
                System.out.println(string1);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    isSameFile

    public static boolean isSameFile(Path path, Path path2) throws IOException
    

    下面是 isSameFile 例子:

    Path path1 = Paths.get("src/main/resources/fileDemo.txt");
    Path path2 = Paths.get("src/main/../main/resources/fileDemo.txt");
    System.out.println(path1.equals(path2));
    try {
        System.out.println(Files.isSameFile(path1, path2));
    } catch (IOException e) {
        e.printStackTrace();
    }
    

    输出:

    false
    true
    

    通过上面的输出信息可以得出即使是相同一个文件但是由于路径不一致,得到的结果是 false

    一个 Path 等于另一个 Path 当且仅当:

    它们有一样FileSystem
    它们具有相同的根元素
    它们具有相同的名称元素
    总结
    本文介绍了 Java11 的新特性有哪些,通过本文可以了解到 Java11 ,在很多领域进步很多。Java11 版本的发布也带来了不少新特性和功能增强、性能提升、基础能力的全面进步和突破。

    参考资料
    11 New Features of Java 11(Java 11的11个新功能)

    https://www.whizlabs.com/blog/java-11-features/
    Java 11 新特性介绍

    https://www.ibm.com/developerworks/cn/java/the-new-features-of-Java-11/index.html
    java11 features

    https://www.journaldev.com/24601/java-11-features
    Java 11字符串类新方法示例

    https://examples.javacodegeeks.com/core-java/java-11-string-class-new-methods-example/
    Write string to file using writeString() API in Java 11

    https://techndeck.com/write-string-to-file-using-writestring-api-in-java-11/