9

Java SE 9 新增特性 - Grey Zeng

 1 year ago
source link: https://www.cnblogs.com/greyzeng/p/16585940.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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:[email protected]">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:[email protected]">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:[email protected]">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:[email protected]">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:[email protected]">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:[email protected]">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:[email protected]">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:[email protected]">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:[email protected]">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:[email protected]">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 

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

Understanding Java 9 Modules

Java 9 模块系统

Multi-Release JAR Files#

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

Creating Multi-Release JAR Files in IntelliJ IDEA

Multi-Release JAR Files with Maven

Java 9 多版本兼容 jar 包

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

具体参考如下代码

package git.snippets.jdk9;

/**
 * 匿名类支持钻石操作符
 *
 * @author <a href="mailto:[email protected]">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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK