Java SE 9 新增特性 - Grey Zeng
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
原文地址:
源码#
镜像仓库: GitCode:java_new_features
JShell#
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增强#
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);
}
}
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
,否则返回一个空的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
包中。
ProcessHandle#
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#
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
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
更多关于模块系统的说明见
Multi-Release JAR Files#
多版本兼容
JAR
功能能让你创建仅在特定版本的Java
环境中运行库程序时选择使用的class
版本。通过--release
参数指定编译版本。
Creating Multi-Release JAR Files in IntelliJ IDEA
Multi-Release JAR Files with Maven
匿名的内部类支持钻石操作符#
具体参考如下代码
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
更多#
参考资料#
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK