作者:Grey
原文地址:
Java SE 9 新增特性
源仓库: Github:java_new_features
镜像仓库: GitCode:java_new_features
JShell
是Java 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
是JDK 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); } }
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); } }
Java SE 9
中,可以将Optional
转为一个Stream
,如果该Optional
中包含值,那么就返回包含这个值的Stream
,否则返回一个空的Stream
(Stream.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
包中。
Java SE 9
的ProcessHandle
接口的实例标识一个本地进程,它允许查询进程状态并管理进程,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
是对变量或参数定义的变量系列的动态强类型引用,包括静态字段,非静态字段,数组元素或堆外数据结构的组件。在各种访问模式下都支持访问这些变量,包括简单的读/写访问,volatile
的读/写访问以及CAS (compare-and-set)
访问。简单来说Variable
就是对这些变量进行绑定,通过Varhandle
直接对这些变量进行操作。Java SE 9
之后,官方推荐使用java.lang.invoke.Varhandle
来替代Unsafe
大部分功能,对比Unsafe
,Varhandle
有着相似的功能,但会更加安全,并且,在并发方面也提高了不少性能。
代码示例
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
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 模块系统
多版本兼容
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