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

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

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: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

Java SE 9 新增特性的更多相关文章

  1. Java SE 8 新增特性

    Java SE 8 新增特性 作者:Grey 原文地址: Java SE 8 新增特性 源码 源仓库: Github:java_new_features 镜像仓库: GitCode:java_new_ ...

  2. Java SE 10 新增特性

    Java SE 10 新增特性 作者:Grey 原文地址:Java SE 10 新增特性 源码 源仓库: Github:java_new_features 镜像仓库: GitCode:java_new ...

  3. Java SE 11 新增特性

    Java SE 11 新增特性 作者:Grey 原文地址:Java SE 11 新增特性 源码 源仓库: Github:java_new_features 镜像仓库: GitCode:java_new ...

  4. Java SE 12 新增特性

    Java SE 12 新增特性 作者:Grey 原文地址:Java SE 12 新增特性 源码 源仓库: Github:java_new_features 镜像仓库: GitCode:java_new ...

  5. Java SE 13 新增特性

    Java SE 13 新增特性 作者:Grey 原文地址:Java SE 13 新增特性 源码 源仓库: Github:java_new_features 镜像仓库: GitCode:java_new ...

  6. Java SE 14 新增特性

    Java SE 14 新增特性 作者:Grey 原文地址:Java SE 14 新增特性 源码 源仓库: Github:java_new_features 镜像仓库: GitCode:java_new ...

  7. Java SE 15 新增特性

    Java SE 15 新增特性 作者:Grey 原文地址:Java SE 15 新增特性 源码 源仓库: Github:java_new_features 镜像仓库: GitCode:java_new ...

  8. Java SE 16 新增特性

    Java SE 16 新增特性 作者:Grey 原文地址:Java SE 16 新增特性 源码 源仓库: Github:java_new_features 镜像仓库: GitCode:java_new ...

  9. Java SE 17 新增特性

    Java SE 17 新增特性 作者:Grey 原文地址:Java SE 17 新增特性 源码 源仓库: Github:java_new_features 镜像仓库: GitCode:java_new ...

随机推荐

  1. React简单教程-3-样式

    前言 在上一章 React 简单教程-2-ts 和组件参数 中我们新建的子组件 Displayer 没有样式,显得平平无奇,这一篇我们将给他美化一下. CSS 文件 一般的做法,是在你的组件级目录下新 ...

  2. Docker容器编译安装Nginx

    Docker容器编译安装Nginx,最简单的Nginx配置. 创建容器&进入容器 宿主机2080映射容器的80端口 [root@localhost ~]# docker run -i -d - ...

  3. ngx_http_fastcgi_module 的那些事

    是什么? 顾名思义,是Nginx用来处理FastCGI的模块.FastCGI是什么?这个以后再讲,可以说的是现在LNMP架构里面,PHP一般是以PHP-CGI的形式在运行,它就是一种FastCGI,我 ...

  4. kafka消费

    消费模型 kafka模型使用了 发布/订阅.点对点模型. 消息发布 在producer端,通过分片策略,找到对应topic下面的Partition leader,把消息发送到当前Partition 消 ...

  5. vue开发必须知道的小技巧

    近年来,vue越来越火,使用它的人也越来越多.vue基本用法很容易上手,但是还有很多优化的写法你就不一定知道了.本文列举了一些vue常用的开发技巧.require.context() 在实际开发中,绝 ...

  6. lvm逻辑卷创建及使用

    创建逻辑卷 pvcreate /dev/md0 pvs 查看创建的pv组 pvdisplay /dev/md0 查看磁盘详细信息 添加vg组: 创建vg组: vgcreate vg1 /dev/md0 ...

  7. java 改变图片的DPI

    代码如下: public class test01 { private static int DPI = 300; public static void main(String[] args) { S ...

  8. Netty 如何高效接收网络数据?一文聊透 ByteBuffer 动态自适应扩缩容机制

    本系列Netty源码解析文章基于 4.1.56.Final版本,公众号:bin的技术小屋 前文回顾 在前边的系列文章中,我们从内核如何收发网络数据开始以一个C10K的问题作为主线详细从内核角度阐述了网 ...

  9. freeswitch拨打分机号源代码跟踪

    概述 freeswitch是一款非常好用的开源VOIP软交换平台. 之前我们有介绍过使用fs拨打分机号的方法,其中代码流程是比较复杂的,所以单独开一章介绍. fs拨打分机号,是使用send_dtmf接 ...

  10. 广义径向基网络(RBF网络)