java 11 是继 java8 之后的第一个LTS版本。因此有必要针对它进行一些深入的学习,虽然短时间内java8 还是主流版本。当然,如果从java8基础上升级,几乎可以确定目标就是java11。
同时也要明确一个问题,现在java的版本升级周期与前些年相比速度快了太多,对于应用开发者来说没必要每一个小版本都去花时间研究,比如这些过渡版本:java9、java10、java12、java13(至少目前还不是LTS版本),了解即可。
下面梳理一下 java11 的新特性。
http://openjdk.java.net/jeps/181
规范中提到了一个例子:
We will adjust the JVM's access rules by adding something like the following clause to JVMS 5.4.4:
A field or method R is accessible to a class or interface D if and only if any of the following conditions are true:
...
R is private and is declared in a different class or interface C, and C and D, are nestmates.
For types C and D to be nestmates they must have the same nest host. A type C claims to be a member of the nest hosted by D, if it lists D in its NestHost attribute. The membership is validated if D also lists C in its NestMembers attribute. D is implicitly a member of the nest that it hosts.
A class with no NestHost or NestMembers attribute, implicitly forms a nest with itself as the nest host, and sole nest member.
简单的翻译一下重点部分:
当且仅当以下条件为真时,一个成员字段或方法R是可以被类或接口D访问的:
...
R 是私有的,并且是声明在另一个类或接口C中,同时 C 和 D 是嵌套伙伴。
由于C和D是嵌套同伴,那么他们一定有同一个嵌套宿主类,在这里是Test。如果C在自己的嵌套宿主的属性中可以列举出D,那么C类就会被D 断言为自己的嵌套成员。如果D也可以在自己的NestMembers 属性中列举出C,那么这个嵌套同伴关系就是有效的。D是自己的隐性嵌套成员。
一个类如果没有 NestHost 或 NestMembers 属性,就会隐性地把自己做为嵌套宿主,以及唯一的嵌套成员。(这和上一句是一致的)
巴拉了一堆,看一个例子:
import java.lang.reflect.Field;
public class Test {
public static void main(String[] args) throws Exception {
System.out.println("---D 的嵌套宿主");
System.out.println(D.class.getNestHost());
System.out.println("---D 的嵌套成员");
for(Class cls:D.class.getNestMembers()) System.out.println(cls);
System.out.println("---C 的嵌套宿主");
System.out.println(C.class.getNestHost());
System.out.println("---C 的嵌套成员");
for(Class cls:C.class.getNestMembers()) System.out.println(cls);
System.out.println("---");
}
static class C{
}
static class D{
}
}
执行结果:
---D 的嵌套宿主
class Test
---D 的嵌套成员
class Test
class Test$D
class Test$C
---C 的嵌套宿主
class Test
---C 的嵌套成员
class Test
class Test$D
class Test$C
---
从该实例中可以看出:
继续来研究这个话题,java 11 解决了什么问题?
import java.lang.reflect.Field;
public class Test {
public static void main(String[] args) throws Exception {
D d=new D();
d.R();
}
static class C{
private int flag=0;
}
static class D{
public void R() throws Exception {
C c=new C();
c.flag=1;
System.out.println(c.flag);// ① 这里没有问题
Field f = C.class.getDeclaredField("flag");
f.setInt(c, 2);
System.out.println(c.flag);//② jdk8 抛出 IllegalAccessException,jdk11 正常
}
}
}
在java8中,注释① 处的代码是没有问题的,因为嵌套类是可以访问别的嵌套类的私有属性的。
但注释② 处的代码会抛出异常,这是一个令人看困惑的问题。java11 修复了这个问题,以上代码在java11中执行正常。
http://openjdk.java.net/jeps/309
这是针对类加载机制的修改,增加了一个名为 CONSTANT_Dynamic 的常量池实体。CONSTANT_Dynamic常量池条目对bootstrap方法进行编码,以执行解析(一个方法句柄)、常量的类型(一个类)和任何静态bootstrap参数(常量的任意序列,在动态常量之间的常量池中排除周期)。
该方案是为了降低创建新形式类文件常量的代价和干扰,为语言设计者和编译器开发者提供更广泛的表现形式以及性能。
http://openjdk.java.net/jeps/315
AArch64是ARMv8 架构的一种执行状态。
在AArch64 CPU指令集中,改进了存在的string和array的内联函数,包括:String::compareTo
, String::indexOf
, StringCoding::hasNegatives
, Arrays::equals
, StringUTF16::compress
, StringLatin1::inflate
以及多样的 checksum
计算方法。
实现了 java.lang.Math
中 sin
, cos
以及 log()
等函数。
http://openjdk.java.net/jeps/318
开发了一个GC,它处理内存分配,但不实现任何实际的内存回收机制。一旦可用的Java堆耗尽,JVM就会关闭。
Epsilon GC 通过 -XX:+UseEpsilonGC
开关启用。
http://openjdk.java.net/jeps/320
从Java SE平台和jdk中移除了 Java EE
和 CORBA
模块,他们在java9中已经标记为 @Deprecated
了。
模块包括:JAX-WS、JAXB、JAF 和 Common Annotation
.
具体的技术组件:
java.xml.ws (JAX-WS, plus the related technologies SAAJ and Web Services Metadata)
java.xml.bind (JAXB)
java.activation (JAF)
java.xml.ws.annotation (Common Annotations)
java.corba (CORBA)
java.transaction (JTA)
相关的包:
java.se.ee (Aggregator module for the six modules above)
jdk.xml.ws (Tools for JAX-WS)
jdk.xml.bind (Tools for JAXB)
共计九个jmods:
Their source code will be deleted from the OpenJDK repository.
Their classes will not exist in the JDK runtime image.
Their tools will no longer be available:
wsgen and wsimport (from jdk.xml.ws)
schemagen and xjc (from jdk.xml.bind)
idlj, orbd, servertool, and tnamesrv (from java.corba)
The JNDI CosNaming provider (from java.corba) will no longer be available.
No command line flag will be capable of enabling them, as --add-modules does on JDK 9.
http://openjdk.java.net/jeps/321
HTTPClient 是在java9引入孵化的项目,在java10中通过JEP110进行了更新,最终在java11通过JEP321 形成标准。
该API通过 CompletableFutures 实现了非阻塞的request和response功能。
背压和流控制是在 java.util.concurrent.Flow
API中通过 reactive-streams 平台实现的。
JEP 321 几乎完全重写了以前的孵化API,实现了完全的异步(以前的HTTP/1.1的实现是阻塞的)。
模块名和包名是: java.net.http
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.URI;
import java.time.Duration;
import java.nio.file.Path;
import java.nio.file.Paths;
public class HttpSyncDemo{
public static void main(String[] args) throws Exception {
HttpClient httpClient = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("http://www.baidu.com"))
.timeout(Duration.ofSeconds(20))
.header("Content-Type", "text/html")
.build();
HttpResponse<Path> response =
httpClient.send(request, HttpResponse.BodyHandlers.ofFile(Paths.get("baidu.txt")));
System.out.println("Response status code: " + response.statusCode());
System.out.println("Response headers: " + response.headers());
System.out.println("Response body: " + response.body());
}
}
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.util.concurrent.CompletableFuture;
import java.net.http.HttpResponse;
import java.net.URI;
import java.time.Duration;
import java.nio.file.Path;
import java.nio.file.Paths;
public class HttpAsyncDemo{
public static void main(String[] args) throws Exception {
HttpClient httpClient = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("http://www.baidu.com"))
.timeout(Duration.ofSeconds(20))
.header("Content-Type", "text/html")
.build();
CompletableFuture<String> response =
httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body);
response.whenComplete((resp, t) -> {
if (t != null) {
System.out.println("response message:" + t.getMessage());
} else {
System.out.println("response:" + resp);
}
}).join();
}
}
在java10 中已经引入了本地变量的类型推断:
var greeting="helloworld!";
System.out.println(greeting);
在java11中类型推断得到了增强,开发者可以使用 var 声明 lambda 参数。
// Inference von Lambda-Parametern
Consumer<String> printer = (var s) -> System.out.println(s); // statt s -> System.out.println(s);
http://openjdk.java.net/jeps/324
密钥交换协议规范 RFC 7748 定义了比 elliptic curve Diffie-Hellman (ECDH) 更高效、更安全的密钥协议模式,JEP324是该协议的具体实现。
KeyPairGenerator kpg = KeyPairGenerator.getInstance("XDH");
NamedParameterSpec paramSpec = new NamedParameterSpec("X25519");
kpg.initialize(paramSpec); // equivalent to kpg.initialize(255)
// alternatively: kpg = KeyPairGenerator.getInstance("X25519")
KeyPair kp = kpg.generateKeyPair();
KeyFactory kf = KeyFactory.getInstance("XDH");
BigInteger u = ...
XECPublicKeySpec pubSpec = new XECPublicKeySpec(paramSpec, u);
PublicKey pubKey = kf.generatePublic(pubSpec);
KeyAgreement ka = KeyAgreement.getInstance("XDH");
ka.init(kp.getPrivate());
ka.doPhase(pubKey, true);
byte[] secret = ka.generateSecret();
什么是密码交换协议?为了数据交互的安全,当客户端和服务端进行交互前,二者需要和对方约定一个密码用来加密解密,那么这个交换密钥(握手)的过程中如何保证密钥不被拦截?这就是密码交换协议解决的范畴。
http://openjdk.java.net/jeps/327
升级平台API,以支持 unicode规范 的 10.0 版本。
设计的类有:java.lang.Character
,java.lang.String
,java.awt.font.NumericShaper
,java.text.Bidi
,java.text.BreakIterator
以及 java.text.Normalizer
。
该实现增加了 16,018
个字符,和10个新的脚本。
http://openjdk.java.net/jeps/328
为java应用程序和 HotSpot JVM 提供了一个低开销的排错框架。
发送事件:
import jdk.jfr.*;
public class Hello{
@Label("Hello World")
@Description("Helps the programmer getting started")
static class HelloWorld extends Event {
@Label("Message")
String message;
}
public static void main(String... args) throws Exception {
HelloWorld event = new HelloWorld();
event.message = "hello, world!";
event.commit();
Thread.sleep(30*60*1000);
}
}
执行如下命令在启动应用程序时记录:
$ java -XX:StartFlightRecording Hello
Started recording 1. No limit specified, using maxsize=250MB as default.
Use jcmd 6120 JFR.dump name=1 filename=FILEPATH to copy recording data to file.
编码方式订阅事件:
import java.nio.file.*;
import jdk.jfr.consumer.*;
public class Consumer{
public static void main(String... args) throws Exception{
Path p = Paths.get("recording.jfr");
for (RecordedEvent e : RecordingFile.readAllEvents(p)) {
try{
System.out.println(e.getStartTime() + " : " + e.getValue("message"));
}catch(Exception ee){
//ee.printStackTrace();
}
}
}
}
输出:
2019-10-18T08:20:37.755218322Z : hello, world!
根据提示执行jcmd 完成记录。
$ jcmd <pid> JFR.start
$ jcmd <pid> JFR.dump filename=recording.jfr
$ jcmd <pid> JFR.stop
http://openjdk.java.net/jeps/329
实现了 RFC 7539 规范中的 ChaCha20 和 ChaCha20-Poly1305 两种加密算法。
ChaCha20 是新式的流加密算法,可以代替陈旧的,不太安全的 RC4 流加密算法。
ChaCha20 测试:
// Get a Cipher instance and set up the parameters
// Assume SecretKey "key", 12-byte nonce "nonceBytes" and plaintext "pText"
// are coming from outside this code snippet
Cipher mambo = Cipher.getInstance("ChaCha20");
ChaCha20ParameterSpec mamboSpec
= new ChaCha20ParameterSpec(nonceBytes, 7); // Use a starting counter value of "7"
// Encrypt our input
mambo.init(Cipher.ENCRYPT_MODE, key, mamboSpec);
byte[] encryptedResult = mambo.doFinal(pText);
ChaCha20-Poly1305 测试:
// Get a Cipher instance and set up the parameters
// Assume SecretKey "key", 12-byte nonce "nonceBytes" and plaintext "pText"
// are coming from outside this code snippet
Cipher mambo = Cipher.getInstance("ChaCha20-Poly1305");
AlgorithmParameterSpec mamboSpec = new IvParameterSpec(nonceBytes);
// Encrypt our input
mambo.init(Cipher.ENCRYPT_MODE, key, mamboSpec);
byte[] encryptedResult = new byte[mambo.getOutputSize(pText.length)];
mambo.doFinal(pText, 0, pText.length, encryptedResult);
可以启动尚未编译的类文件。
java HelloWorld.java
它相当于:
javac -d <memory> HelloWorld.java
java -cp <memory> HelloWorld
同时,java11也引入了 Shebang(#!
)机制可以直接编写java可执行脚本,如:以下是 demo.sh
的脚本:
#!/usr/local/jdk/bin/java --source 11
public class HelloWorld{
public static void main(String[] args){
System.out.println("helloworld");
}
}
执行:
# chmod +x demo.sh
# ./demo.sh
helloworld
env的语法:
#!/usr/bin/env 脚本解释器名称
如果编写如下脚本:
#!/usr/bin/env java --source 11
执行会出现错误,因为 java --source 11
被当做了一个整体传递给了 env.
http://openjdk.java.net/jeps/331
提供了一个低开销的堆分配采样方法,可以使用JVMTI 进行访问。
http://openjdk.java.net/jeps/332
了解 https的开发者都应该知道TLS,这是 TLS1.3 的实现。
http://openjdk.java.net/jeps/333
一个新的低延迟的垃圾回收期,该版本提供的是实验性特性。
http://openjdk.java.net/jeps/335
将 Nashorn javascript 引擎标记为废弃,以后的版本会被删除。世事难料,它是java8才被引入的,这就废弃了,至于原因,规范中说:
With the rapid pace at which ECMAScript language constructs, along with APIs, are adapted and modified, we have found Nashorn challenging to maintain.
大致意思是:随着ECMAScript语言结构和api的快速调整和修改,我们发现很难维护Nashorn。
当版本帝遇到版本帝,一山不容二帝,byebye.
http://openjdk.java.net/jeps/336
将pack200 和 unpack200 工具标记为弃用,同时弃用的还有 java.util.jar
中的API。
pack200 是什么?它在java的生命中很重要。pack200 是jar文件的压缩模式,它当初的目标是"在java应用程序打包、传输、交付过程中减少磁盘存储和带宽需求"。
这么重要的工具为什么要废弃,官方给出了三个原因: