• Java SE 9 新增特性


    Java SE 9 新增特性

    作者:Grey

    原文地址:

    Java SE 9 新增特性

    源码

    源仓库: Github:java_new_features

    镜像仓库: GitCode:java_new_features

    JShell

    JShellJava SE 9新增的一个交互式的编程环境工具。它允许你无需使用类或者方法包装来执行Java语句。它与Python的解释器类似,可以直接输入表达式并查看其执行结果。

    在控制台输入jshell命令并回车,注:需要配置jdk的环境变量,jdk版本要大于或等于9

    基本用法

    C:\Users\Young>jshell
    |  欢迎使用 JShell -- 版本 17.0.4
    |  要大致了解该版本, 请键入: /help intro
    
    jshell> System.out.println("hello shell");
    hello shell
    
    jshell> 1 + 2
    $2 ==> 3
    
    jshell> Math.pow(3,2)
    $3 ==> 9.0
    
    jshell> void p(String s){System.out.println(s);}
    |  已创建 方法 p(String)
    
    jshell> p("hello shell");
    hello shell
    
    

    更多介绍参考:Introduction to JShell

    try-with-resources增强

    try-with-resourcesJDK 7中一个新的异常处理机制,它能够很容易地关闭在try-catch语句块中使用的资源(所有实现了java.lang.AutoCloseable接口和java.io.Closeable的对象都可以是资源)。

    try-with-resources声明在JDK 9已得到改进。如果你已经有一个资源是final或等效于final变量,可以在try-with-resources语句中使用该变量,而无需在try-with-resources语句中声明一个新变量。

    实例

    package git.snippets.jdk9;
    
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.Reader;
    import java.io.StringReader;
    
    /**
     * try-with-resources增强
     *
     * @since 1.9
     */
    public class TryWithResourceDemo {
        public static void main(String[] args) throws IOException {
            System.out.println(readDataPreJDK9("test"));
            System.out.println(readDataInJDK9("test"));
        }
    
        static String readDataPreJDK9(String message) throws IOException {
            Reader inputString = new StringReader(message);
            BufferedReader br = new BufferedReader(inputString);
            try (BufferedReader br1 = br) {
                return br1.readLine();
            }
        }
    
        static String readDataInJDK9(String message) throws IOException {
            Reader inputString = new StringReader(message);
            BufferedReader br = new BufferedReader(inputString);
            try (br) {
                return br.readLine();
            }
        }
    }
    
    

    readDataPreJDK9方法是Java 9之前的做法,需要在try语句块中声明资源br1,然后才能使用它。

    Java 9中,我们不需要声明资源br1就可以使用它,并得到相同的结果。见readDataInJDK9方法。

    创建不可变集合

    package git.snippets.jdk9;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Map;
    import java.util.Set;
    
    /**
     * 创建不可变集合
     *
     * @author <a href="mailto:410486047@qq.com">Grey</a>
     * @date 2021/11/28
     * @since 9
     */
    public class ImmutableListTest {
        static void test() {
            List<String> list1 = List.of("a", "b");
            // 创建了不可变的List,不可以执行add操作
            System.out.println(list1); // true
    
            // 创建不可变的Set,不可以执行add操作
            Set<String> set = Set.of("ab", "bc");
            System.out.println(set.size());
    
            // 创建了不可变Map,无法put元素
            Map<String, Integer> map1 = Map.of("a", 2, "b", 3, "c", 4);
            System.out.println(map1);
            Map<String, Integer> map2 = Map.ofEntries(Map.entry("a", 1), Map.entry("b", 2));
            System.out.println(map2);
        }
    }
    

    Stream API

    API 使用方法
    dropWhile 从头开始,遇到不满足就结束,收集剩余的
    takeWhile 从头开始收集,遇到不满足就结束
    iterate 将某个值使用某个方法,直到不满足给定的条件
    ofNullable 如果指定元素为非 null,则获取一个元素并生成单个元素流,元素为 null 则返回一个空流。

    示例代码

    package git.snippets.jdk9;
    
    import java.util.List;
    import java.util.stream.Collectors;
    import java.util.stream.Stream;
    
    /**
     * stream增强
     *
     * @author <a href="mailto:410486047@qq.com">Grey</a>
     * @date 2021/11/29
     * @since 9
     */
    public class StreamEnhanceTest {
        public static void main(String[] args) {
            // dropWhile方法,从头开始删除,遇到不满足的就结束,返回:[3,4,4,0]
            System.out.println(List.of(1, 2, 3, 4, 3, 0).stream().dropWhile(x -> x < 3).collect(Collectors.toList()));
            // takeWhile方法,从头开始筛选,遇到不满足的就结束,返回:[1,2]
            System.out.println(List.of(1, 2, 3, 4, 3, 0).stream().takeWhile(x -> x < 3).collect(Collectors.toList()));
            // iterate方法,收集[0,9] 十个数字,然后打印出来
            Stream.iterate(0, x -> x < 10, x -> x + 1).forEach(System.out::println);
    //        ofNullable 用法
    //        ofNullable 方法可以预防 NullPointerExceptions 异常, 可以通过检查流来避免 null 值。
    //        如果指定元素为非 null,则获取一个元素并生成单个元素流,元素为 null 则返回一个空流。
            long count = Stream.ofNullable(100).count();
            // 非空,返回1
            System.out.println(count);
            count = Stream.ofNullable(null).count();
            // null,返回0
            System.out.println(count);
        }
    }
    

    Optional增强

    Java SE 9中,可以将Optional转为一个Stream,如果该Optional中包含值,那么就返回包含这个值的Stream,否则返回一个空的StreamStream.empty())。

    实例

    package git.snippets.jdk9;
    
    import java.util.Arrays;
    import java.util.List;
    import java.util.Optional;
    import java.util.stream.Collectors;
    import java.util.stream.Stream;
    
    /**
     * Optional增强
     *
     * @author <a href="mailto:410486047@qq.com">Grey</a>
     * @date 2022/8/13
     * @since 9
     */
    public class OptionalDemo {
        public static void main(String[] args) {
            // Optional的stream方法
            List<Optional<String>> list = Arrays.asList(Optional.empty(), Optional.of("A"), Optional.empty(), Optional.of("B"), Optional.ofNullable(null));
            // jdk 9 之前
            list.stream().flatMap(o -> o.isPresent() ? Stream.of(o.get()) : Stream.empty()).forEach(System.out::println);
            // jdk 9 优化后
            list.stream().flatMap(Optional::stream).forEach(System.out::println);
        }
    }
    
    

    执行输出结果为:

    [A, B]
    [A, B]
    

    Java SE 9中新增了ifPresentOrElse()方法,如果一个Optional包含值,则对其包含的值调用函数action,即action.accept(value)ifPresentOrElse还有第二个参数emptyAction,如果Optional不包含值,那么ifPresentOrElse便会调用emptyAction,即emptyAction.run()

    示例代码

    package git.snippets.jdk9;
    
    import java.util.Arrays;
    import java.util.List;
    import java.util.Optional;
    import java.util.stream.Collectors;
    import java.util.stream.Stream;
    
    /**
     * Optional增强
     *
     * @author <a href="mailto:410486047@qq.com">Grey</a>
     * @date 2022/8/13
     * @since 9
     */
    public class OptionalDemo {
        public static void main(String[] args) {
            Optional<Integer> optional = Optional.of(1);
            optional.ifPresentOrElse(x -> System.out.println("Value: " + x), () -> System.out.println("Not Present."));
    
            optional = Optional.empty();
            optional.ifPresentOrElse(x -> System.out.println("Value: " + x), () -> System.out.println("Not Present."));
    
            optional = Optional.ofNullable(null);
            optional.ifPresentOrElse(x -> System.out.println("Value: " + x), () -> System.out.println("Not Present."));
        }
    }
    

    执行输出结果为

    Value: 1
    Not Present.
    Not Present.
    

    Optional中新增了or(),如果值存在,返回Optional指定的值,否则返回一个预设的值。

    示例代码

    package git.snippets.jdk9;
    
    import java.util.Arrays;
    import java.util.List;
    import java.util.Optional;
    import java.util.function.Supplier;
    import java.util.stream.Collectors;
    import java.util.stream.Stream;
    
    /**
     * Optional增强
     *
     * @author <a href="mailto:410486047@qq.com">Grey</a>
     * @date 2022/8/13
     * @since 9
     */
    public class OptionalDemo {
        public static void main(String[] args) {
            // Optional的or方法
            Optional.empty().or(() -> Optional.of("Not Present")).ifPresent(x -> System.out.println("value:" + x));
            Optional.of("hello").or(() -> Optional.of("Not Present")).ifPresent(x -> System.out.println("value:" + x));
        }
    }
    
    

    输出结果为

    value:Not Present
    value:hello
    

    接口私有方法

    Java SE 9开始,接口支持private方法,接口中的private无法被子类重写和调用,但是可以用于内部default方法或者static方法调用。

    示例如下

    package git.snippets.jdk9;
    
    /**
     * @author <a href="mailto:410486047@qq.com">Grey</a>
     * @date 2021/11/29
     * @since
     */
    public class InterfacePrivateTest {
        public static void main(String[] args) {
            X x = new X();
            x.sleep();
            x.eat();
            x.doXxx();
            A.x();
        }
    }
    
    class X implements A {
        @Override
        public void sleep() {
            System.out.println("sleep");
        }
    }
    
    interface A {
        void sleep();
    
        default void eat() {
            sleep();
        }
    
        default void doXxx() {
            drink();
            x();
        }
    
        static void x() {
            System.out.println("x");
        }
    
        private void drink() {
            System.out.println("drink");
            x();
        }
    }
    

    Java SE 8中,接口可以有静态方法的默认实现,例:

    public interface Test {
        public static void print() {
            System.out.println("interface print");
        }
    
        default void pout() {
            System.out.println();
        }
    }
    

    Java SE 9中,可以支持private的静态方法实现,例:

    public interface Test {
        private static void print() {
            System.out.println("interface print");
        }
    
        static void pout() {
            print();
        }
    }
    

    自带HttpClient客户端(孵化阶段)

    package git.snippets.jdk9;
    
    import jdk.incubator.http.HttpClient;
    import jdk.incubator.http.HttpRequest;
    import jdk.incubator.http.HttpResponse;
    
    import java.io.IOException;
    import java.net.URI;
    
    /**
     * 注意:添加module-info信息
     * jdk11已经把包移入:java.net.http
     *
     * @author <a href="mailto:410486047@qq.com">Grey</a>
     * @date 2022/8/14
     * @since 9
     */
    public class HttpClientTestJDK9 {
        public static void main(String[] args) throws IOException, InterruptedException {
            HttpClient client = HttpClient.newHttpClient();
            URI uri = URI.create("http://httpbin.org/get");
            HttpRequest req = HttpRequest.newBuilder(uri).header("accept", "application/json").GET().build();
            HttpResponse<String> resp = client.send(req, HttpResponse.BodyHandler.asString());
            String body = resp.body();
            System.out.println(body);
        }
    }
    

    注:执行上述代码时候,jdk.incubator.httpclient需要通过module-info.java引入进来

    module git.snippets.jdk9 {
        requires jdk.incubator.httpclient;
    }
    

    或者在javac的时候,通过

    --add-modules jdk.incubator.httpclient 
    

    引入这个模块。

    jdk9中的HttpClient还在jdk.incubator.httpclient包中,jdk11已移动到java.net.http包中

    ProcessHandle

    Java SE 9ProcessHandle接口的实例标识一个本地进程,它允许查询进程状态并管理进程,onExit()方法可用于在某个进程终止时触发某些操作。

    package git.snippets.jdk9;
    
    import java.io.IOException;
    import java.util.stream.Collectors;
    
    
    /**
     * 获取进程相关信息
     *
     * @author <a href="mailto:410486047@qq.com">Grey</a>
     * @date 2022/8/14
     * @since 9
     */
    public class ProcessHandlerDemo {
        public static void main(String[] args) throws IOException {
            // 获取所有未结束的进程信息并打印
            ProcessHandle.allProcesses().filter(ProcessHandle::isAlive).collect(Collectors.toSet()).forEach(s -> System.out.println(s.info().command().get()));
    
            Runtime rt = Runtime.getRuntime();
            // FIXME 可以替换成你本地的一个进程名称
            Process p = rt.exec("java.exe");
            ProcessHandle pro = p.toHandle();
            p.onExit().thenRunAsync(() -> System.out.println("程序退出之后执行"));
            pro.supportsNormalTermination();
            if (pro.destroyForcibly()) {
                System.out.println("摧毁进程:" + pro.pid());
            }
            System.out.println(pro.isAlive());
        }
    }
    

    VarHandle

    Varhandle是对变量或参数定义的变量系列的动态强类型引用,包括静态字段,非静态字段,数组元素或堆外数据结构的组件。在各种访问模式下都支持访问这些变量,包括简单的读/写访问,volatile的读/写访问以及CAS (compare-and-set)访问。简单来说Variable就是对这些变量进行绑定,通过Varhandle直接对这些变量进行操作。Java SE 9之后,官方推荐使用java.lang.invoke.Varhandle来替代Unsafe大部分功能,对比UnsafeVarhandle有着相似的功能,但会更加安全,并且,在并发方面也提高了不少性能。

    代码示例

    package git.snippets.jdk9;
    
    import java.lang.invoke.MethodHandles;
    import java.lang.invoke.VarHandle;
    import java.util.Arrays;
    
    /**
     * VarHandle使用
     *
     * @author <a href="mailto:410486047@qq.com">Grey</a>
     * @date 2022/8/14
     * @since 9
     */
    public class VarHandleDemo {
        public static void main(String[] args) throws Exception {
            Data instance = new Data();
            System.out.println(instance);
            MethodHandles.privateLookupIn(Data.class, MethodHandles.lookup()).findVarHandle(Data.class, "privateVar", int.class).set(instance, 11);
            MethodHandles.privateLookupIn(Data.class, MethodHandles.lookup()).findVarHandle(Data.class, "publicVar", int.class).set(instance, 22);
            MethodHandles.privateLookupIn(Data.class, MethodHandles.lookup()).findVarHandle(Data.class, "protectedVar", int.class).set(instance, 33);
            VarHandle arrayVarHandle = MethodHandles.arrayElementVarHandle(int[].class);
            arrayVarHandle.compareAndSet(instance.arrayData, 0, 1, 111);
            arrayVarHandle.compareAndSet(instance.arrayData, 1, 2, 222);
            arrayVarHandle.compareAndSet(instance.arrayData, 2, 3, 333);
            System.out.println(instance);
        }
    }
    
    class Data {
        public int publicVar = 1;
        protected int protectedVar = 2;
        private int privateVar = 3;
        public int[] arrayData = new int[]{1, 2, 3};
    
        @Override
        public String toString() {
            return "Data{" + "publicVar=" + publicVar + ", protectedVar=" + protectedVar + ", privateVar=" + privateVar + ", arrayData=" + Arrays.toString(arrayData) + '}';
        }
    }
    

    输出结果

    Data{publicVar=1, protectedVar=2, privateVar=3, arrayData=[1, 2, 3]}
    Data{publicVar=22, protectedVar=33, privateVar=11, arrayData=[111, 222, 333]}
    

    更多VarHandle见:Class VarHandle

    StackWalker

    Java SE 9以前堆栈遍历

    StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
    

    抛出异常并从中提取堆栈跟踪信息。

    new Exception().printStackTrace();
    

    Java SE 9提供了一种新的API使用遍历堆栈。

    StackWalker stack = StackWalker.getInstance();
    

    如果我们想要遍历整个堆栈,那只需要调用forEach()方法:

    stack.forEach(System.out::println);
    

    使用示例

    package git.snippets.jdk9;
    
    import java.util.Scanner;
    
    /**
     * StackWalker使用
     *
     * @author <a href="mailto:410486047@qq.com">Grey</a>
     * @date 2022/8/14
     * @since 9
     */
    public class StackWalkerDemo {
        /**
         * Computes the factorial of a number
         *
         * @param n a non-negative integer
         * @return n! = 1 * 2 * . . . * n
         */
        public static int factorial(int n) {
            System.out.println("factorial(" + n + "):");
            StackWalker walker = StackWalker.getInstance();
            walker.forEach(System.out::println);
            int r;
            if (n <= 1) {
                r = 1;
            } else {
                r = n * factorial(n - 1);
            }
            System.out.println("return " + r);
            return r;
        }
    
        public static void main(String[] args) {
            try (Scanner in = new Scanner(System.in)) {
                System.out.print("Enter n: ");
                int n = in.nextInt();
                int result = factorial(n);
                System.out.println(result);
            }
        }
    }
    

    运行,输入n=4,可以看到控制台打印了堆栈信息

    Enter n: 4
    factorial(4):
    git.snippets.jdk9/git.snippets.jdk9.StackWalkerDemo.factorial(StackWalkerDemo.java:22)
    git.snippets.jdk9/git.snippets.jdk9.StackWalkerDemo.main(StackWalkerDemo.java:37)
    factorial(3):
    git.snippets.jdk9/git.snippets.jdk9.StackWalkerDemo.factorial(StackWalkerDemo.java:22)
    git.snippets.jdk9/git.snippets.jdk9.StackWalkerDemo.factorial(StackWalkerDemo.java:27)
    git.snippets.jdk9/git.snippets.jdk9.StackWalkerDemo.main(StackWalkerDemo.java:37)
    factorial(2):
    git.snippets.jdk9/git.snippets.jdk9.StackWalkerDemo.factorial(StackWalkerDemo.java:22)
    git.snippets.jdk9/git.snippets.jdk9.StackWalkerDemo.factorial(StackWalkerDemo.java:27)
    git.snippets.jdk9/git.snippets.jdk9.StackWalkerDemo.factorial(StackWalkerDemo.java:27)
    git.snippets.jdk9/git.snippets.jdk9.StackWalkerDemo.main(StackWalkerDemo.java:37)
    factorial(1):
    git.snippets.jdk9/git.snippets.jdk9.StackWalkerDemo.factorial(StackWalkerDemo.java:22)
    git.snippets.jdk9/git.snippets.jdk9.StackWalkerDemo.factorial(StackWalkerDemo.java:27)
    git.snippets.jdk9/git.snippets.jdk9.StackWalkerDemo.factorial(StackWalkerDemo.java:27)
    git.snippets.jdk9/git.snippets.jdk9.StackWalkerDemo.factorial(StackWalkerDemo.java:27)
    git.snippets.jdk9/git.snippets.jdk9.StackWalkerDemo.main(StackWalkerDemo.java:37)
    return 1
    return 2
    return 6
    return 24
    24
    

    模块系统

    Java SE 9引入了模块系统,模块就是代码和数据的封装体。模块的代码被组织成多个包,每个包中包含Java类和接口;模块的数据则包括资源文件和其他静态信息。

    module-info.java文件中,我们可以用新的关键词module来声明一个模块。

    如上示例中的HttpClient客户端代码,

    执行代码时候,jdk.incubator.httpclient需要通过module-info.java引入进来

    module git.snippets.jdk9 {
        requires jdk.incubator.httpclient;
    }
    

    或者在javac的时候,通过

    --add-modules jdk.incubator.httpclient 
    

    更多关于模块系统的说明见

    Java SE 9 模块化示例

    Multi-Release JAR Files

    多版本兼容JAR功能能让你创建仅在特定版本的Java环境中运行库程序时选择使用的class版本。通过--release参数指定编译版本。

    Java SE 9 多版本兼容 JAR 包示例

    匿名的内部类支持钻石操作符

    具体参考如下代码

    package git.snippets.jdk9;
    
    /**
     * 匿名类支持钻石操作符
     *
     * @author <a href="mailto:410486047@qq.com">Grey</a>
     * @date 2022/8/14
     * @since 9
     */
    public class DiamondEnhanceDemo {
        public static void main(String[] args) {
            // jdk9之前
            Handler<Integer> preJdk9 = new Handler<Integer>(1) {
                @Override
                public void handle() {
                    System.out.println(content);
                }
            };
            // jdk9及以上版本
            Handler<Integer> jdk9Above = new Handler<>(1) {
                @Override
                public void handle() {
                    System.out.println(content);
                }
            };
        }
    
    
    }
    
    abstract class Handler<T> {
        public T content;
    
        public Handler(T content) {
            this.content = content;
        }
    
        abstract void handle();
    }
    

    其他更新

    Java SE 9开始,无法用单个下划线作为变量名称

    int _ = 3; // java9 or above , error
    

    Objects.requireNonNullElse方法

    String a = Objects.requireNonNullElse(m,"Bc"); // 若m不为null,则a = m,若m为null,则a = "Bc"
    

    在命令行参数中,Java SE 9之前指定classpath通过如下参数

    -cp
    

    或者

    -classpath
    

    Java SE 9新增了

    --class-path
    

    更多命令参数,见jeps

    更多

    Java SE 7及以后各版本新增特性,持续更新中...

    参考资料

    Java Language Updates

    Java Platform, Standard Edition What’s New in Oracle JDK 9

    Java 新特性教程

    Java 9 新特性

    Creating Multi-Release JAR Files in IntelliJ IDEA

  • 相关阅读:
    浙江工业大学校赛 小马哥和数列
    浙江工业大学校赛 XiaoWei的战斗力
    浙江工业大学校赛 猜猜谁是我
    浙江工业大学校赛 竹之书(大数,同余定理)
    浙江工业大学校赛 画图游戏 BugZhu抽抽抽!!
    浙江工业大学校赛 画图游戏
    pta 天梯地图 (Dijkstra)
    Atom打造 c/c++编译环境(忙了一个上午)
    HRBUST
    CSU 1808 地铁 (Dijkstra)
  • 原文地址:https://www.cnblogs.com/greyzeng/p/16585940.html
Copyright © 2020-2023  润新知