Hello World

吞风吻雨葬落日 欺山赶海踏雪径

0%

Java 11 新特性

java 11 是java 8 之后的第一个 long-term support (LTS) 版本。Java10是最后一个免费的Oracle JDK版本,我们可以在没有许可证的情况下进行商业使用。
从Java 11开始,Oracle不提供免费的长期支持。Oracle继续提供Open JDK发布,我们可以下载免费使用。Oracle JDK 与 OpenJDK 的区别

jdk 11 主要的新特性参考:
openjdk JDK 11

baeldung New Features in Java 11

JDK 11 的新特性

  • 181: Nest-Based Access Control (基于嵌套的访问控制)
  • 309: Dynamic Class-File Constants (动态类文件常量)
  • 315: Improve Aarch64 Intrinsics
  • 318: Epsilon: A No-Op Garbage Collector (Epsilon 垃圾回收器,又被称为 No-Op(无操作)回收器)
  • 320: Remove the Java EE and CORBA Modules (移除 JavaEE 和 CORBA 模块, JavaFx 模块也被移除了)
  • 321: HTTP Client (Standard) (新的HTTP Client (java 9 孵化))
  • 323: Local-Variable Syntax for Lambda Parameters (Lambda 参数的局部变量语法)
  • 324: Key Agreement with Curve25519 and Curve448
  • 327: Unicode 10
  • 328: Flight Recorder (飞行记录仪特性)
  • 329: ChaCha20 and Poly1305 Cryptographic Algorithms
  • 330: Launch Single-File Source-Code Programs (运行单个Java源码文件)
  • 331: Low-Overhead Heap Profiling (底开销的堆分析方法)
  • 332: Transport Layer Security (TLS) 1.3 (对 TLS 1.3支持)
  • 333: ZGC: A Scalable Low-Latency Garbage Collector(Experimental) (ZGC 实验阶段)
  • 335: Deprecate the Nashorn JavaScript Engine (弃用 Nashorn JavaScript 引擎)
  • 336: Deprecate the Pack200 Tools and API (弃用 Pack200 工具以及 API)

下面针对比较重要的特性做说明。

开发者特性

String类新方法

Java 11 String 类增加了一个新方法:isBlank, lines, strip, stripLeading, stripTrailing, and repeat.

1
2
3
4
5
6
7
8
9
10
11
12
// 判断字符串是否为空白
" ".isBlank(); // true
// 去除首尾空格
" Javastack ".strip(); // "Javastack"
// 去除尾部空格
" Javastack ".stripTrailing(); // " Javastack"
// 去除首部空格
" Javastack ".stripLeading(); // "Javastack "
// 复制字符串
"Java".repeat(3);// "JavaJavaJava"
// 行数统计
"A\nB\nC".lines().count(); // 3

File类新方法

新增File类新的的静态方法:readStringwriteString 使得文件的读写更加容易。

1
2
3
4
5
6
7
8
Files.writeString(
Path.of("./", "tmp.txt"), // 路径
"hello, jdk11 files api", // 内容
StandardCharsets.UTF_8); // 编码

String s = Files.readString(
Paths.get("./tmp.txt"), // 路径
StandardCharsets.UTF_8); // 编码

集合转数组

java.util.Collection 接口有一个新的默认方法:toArray

1
2
3
default <T> T[] toArray(IntFunction<T[]> generator) {
return toArray(generator.apply(0));
}

可以更简单的创建对应的类型的数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
    List<String> sampleList = Arrays.asList("Java", "Kotlin");
// old method
Object[] objectArray = sampleList.toArray();
System.out.println("toArray() size :" + objectArray.length);

String[] sampleArray = new String[]{"1","2","3","4"};
// enough space even bigger
sampleArray = sampleList.toArray(sampleArray);
System.out.println("toArray(T[] a) size :" + sampleArray.length + " , content :");
for (String s : sampleArray) {
// set position 3 to null .
System.out.println(Objects.toString(s));
}

// new method
// new String [] and not enough space
// sampleList.toArray(String[]::new)
String[] sampleStrArray = sampleList.toArray((var i)-> new String[i]);
System.out.println("toArray(IntFunction<T[]> generator) size :" + sampleStrArray.length + " , content :");
for (String s : sampleStrArray) {
System.out.println(Objects.toString(s));
}

输出

1
2
3
4
5
6
7
8
9
toArray() size :2
toArray(T[] a) size :4 , content :
Java
Kotlin
null // 这个被设置成null了
4
toArray(IntFunction<T[]> generator) size :2 , content :
Java
Kotlin

Not Predicate 方法

Predicate 新增了一个静态not方法,用来对一个存在的Predicate取反。

1
2
3
4
5
List<String> sampleList = Arrays.asList("Java", "\n \n", "Kotlin", " ");
List withoutBlanks = sampleList.stream()
.filter(Predicate.not(String::isBlank))
.collect(Collectors.toList());
assertThat(withoutBlanks).containsExactly("Java", "Kotlin");

not(isBlank)读起来比 isBlank.negate()更优雅,该方法的一大优势是可以像not(String::isBlank)这样用于方法引用。

Lambda本地变量语法

本地变量类型推断 (java 10 加入的特性)在 java 11 中终于支持 lambda了。
并且可以给本地变量添加修饰语,比如添加一个自定义注解。

1
2
3
4
5
List<String> sampleList = Arrays.asList("Java", "Kotlin");
String resultString = sampleList.stream()
.map((@Nonnull var x) -> x.toUpperCase())
.collect(Collectors.joining(", "));
assertThat(resultString).isEqualTo("JAVA, KOTLIN");

HTTP Client

java 9 中孵化的 HTTP Client终于在 java 11 中成为了标准的特性了。

新的HTTP API 提升了整体性能并且支持了HTTP/1.1和 HTTP/2。

1
2
3
4
5
6
7
8
9
10
HttpClient httpClient = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_2)
.connectTimeout(Duration.ofSeconds(20))
.build();
HttpRequest httpRequest = HttpRequest.newBuilder()
.GET()
.uri(URI.create("http://localhost:" + port))
.build();
HttpResponse httpResponse = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString());
assertThat(httpResponse.body()).isEqualTo("Hello from the server!");

运行Java文件

Java 11 不需要用 javac 显示的编译 java 源文件了。

1
2
3
$ javac HelloWorld.java
$ java HelloWorld
Hello Java 8!

现在可以直接运行 HelloWorld.java 源文件

1
2
$ java HelloWorld.java
Hello Java 11!

Nest Based Access Control

Java 11 引入了nestmates概念以及JVM内的相关访问规则。

嵌套是一种访问控制上下文,它允许多个class同属一个逻辑代码块,但是被编译成多个分散的class文件,它们访问彼此的私有成员无需通过编译器添加访问扩展方法。

桥接

在java 11 之前的桥接方法 access$000 :

Alphabet.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Alphabet {

private String name = "I'm Alphabet!";

public class A {
public void printName() {
System.out.println(name); // access Alphabet's private member!
}
}

public class B {
public void printName() {
System.out.println(name); // access Alphabet's private member!
}

public class B1 {
public void printName() {
System.out.println(name); // access Alphabet's private member!
}
}
}
}

编译上述方法会生成四个了类 AlphabetAlphabet$AAlphabet$BAlphabet$B$B1

1
2
> ls
Alphabet$A.class Alphabet$B$B1.class Alphabet$B.class Alphabet.class

使用 javap 查看下反编译的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
> javap  -p Alphabet.class
Compiled from "Alphabet.java"
public class org.example.Alphabet {
private java.lang.String name;
public org.example.Alphabet();
static java.lang.String access$000(org.example.Alphabet);
}

> javap -c Alphabet\$B.class
Compiled from "Alphabet.java"
public class org.example.Alphabet$B {
final org.example.Alphabet this$0;

public org.example.Alphabet$B(org.example.Alphabet);
Code:
0: aload_0
1: aload_1
2: putfield #1 // Field this$0:Lorg/example/Alphabet;
5: aload_0
6: invokespecial #2 // Method java/lang/Object."<init>":()V
9: return

public void printName();
Code:
0: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_0
4: getfield #1 // Field this$0:Lorg/example/Alphabet;
7: invokestatic #4 // Method org/example/Alphabet.access$000:(Lorg/example/Alphabet;)Ljava/lang/String;
10: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
13: return
}

> javap -c Alphabet\$B\$B1.class
Compiled from "Alphabet.java"
public class org.example.Alphabet$B$B1 {
final org.example.Alphabet$B this$1;

public org.example.Alphabet$B$B1(org.example.Alphabet$B);
Code:
0: aload_0
1: aload_1
2: putfield #1 // Field this$1:Lorg/example/Alphabet$B;
5: aload_0
6: invokespecial #2 // Method java/lang/Object."<init>":()V
9: return

public void printName();
Code:
0: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_0
4: getfield #1 // Field this$1:Lorg/example/Alphabet$B;
7: getfield #4 // Field org/example/Alphabet$B.this$0:Lorg/example/Alphabet;
10: invokestatic #5 // Method org/example/Alphabet.access$000:(Lorg/example/Alphabet;)Ljava/lang/String;
13: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
16: return
}

可以看到内部类 B 和 B1 都是通过了 Alphabet 类生成的桥接方法 static java.lang.String access$000(org.example.Alphabet) 来访问 Alphabet 的私有成员 name 的。
但是这种方式其实是存在安全风险的(禁止外部对自动生成access$100(Outer)方法的调用的检查是在编译时期做的)。

所以在 java 11中, 提供了不用通过桥接的方法直接访问嵌套类的私有成员。

java 11 重新编译后:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
> javap -p Alphabet.class
Compiled from "Alphabet.java"
public class org.example.Alphabet {
private java.lang.String name;
public org.example.Alphabet();
}

> javap -c Alphabet\$B.class
Compiled from "Alphabet.java"
public class org.example.Alphabet$B {
final org.example.Alphabet this$0;

public org.example.Alphabet$B(org.example.Alphabet);
Code:
0: aload_0
1: aload_1
2: putfield #1 // Field this$0:Lorg/example/Alphabet;
5: aload_0
6: invokespecial #7 // Method java/lang/Object."<init>":()V
9: return

public void printName();
Code:
0: getstatic #13 // Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_0
4: getfield #1 // Field this$0:Lorg/example/Alphabet;
7: getfield #19 // Field org/example/Alphabet.name:Ljava/lang/String;
10: invokevirtual #25 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
13: return
}
> javap -c Alphabet\$B\$B1.class
Compiled from "Alphabet.java"
public class org.example.Alphabet$B$B1 {
final org.example.Alphabet$B this$1;

public org.example.Alphabet$B$B1(org.example.Alphabet$B);
Code:
0: aload_0
1: aload_1
2: putfield #1 // Field this$1:Lorg/example/Alphabet$B;
5: aload_0
6: invokespecial #7 // Method java/lang/Object."<init>":()V
9: return

public void printName();
Code:
0: getstatic #13 // Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_0
4: getfield #1 // Field this$1:Lorg/example/Alphabet$B;
7: getfield #19 // Field org/example/Alphabet$B.this$0:Lorg/example/Alphabet;
10: getfield #25 // Field org/example/Alphabet.name:Ljava/lang/String;
13: invokevirtual #31 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
16: return
}

Alphabet 类并没有生成桥接方法,且内部类 B 与 B1 都是直接访问的 Alphabet.name

同时 java 11 提供了一系列嵌套类的API

  • getNestHost()
  • getNestMembers()
  • isNestmateOf()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// com.forg.java11.bean.Outer
System.out.println(Outer.class.getNestHost().getName());
// com.forg.java11.bean.Outer
System.out.println(Outer.InnerB.class.getNestHost().getName());
// com.forg.java11.bean.Outer
System.out.println(Outer.InnerB.InnerBB.class.getNestHost().getName());

// true
System.out.println(Outer.InnerB.class.isNestmateOf(Outer.class));
// true
System.out.println(Outer.InnerB.InnerBB.class.isNestmateOf(Outer.class));

// com.forg.java11.bean.Outer
// com.forg.java11.bean.Outer$InnerB
// com.forg.java11.bean.Outer$InnerB$InnerBB
// com.forg.java11.bean.Outer$InnerA
Arrays.stream(Outer.class.getNestMembers()).map(Class::getName).forEach(
System.out::println
);
// the same
Arrays.stream(Outer.InnerB.InnerBB.class.getNestMembers()).map(Class::getName).forEach(
System.out::println
);

JVM访问规则允许nestmates间的私有成员的访问,在之前的Java版本,reflection API 会拒绝了访问并抛出 java.lang.IllegalAccessException 异常,Java11 修复了这个问题 。

性能提升

再看一些以性能提升为目的的新特性。

Dynamic Class-File Constants

新的常量池格式 CONSTANT_Dynamic 旨在降低消耗与减少中断。

Improved Aarch64 Intrinsics

JEP 315

Java 11 optimizes the existing string and array intrinsics on ARM64 or AArch64 processors. Additionally, new intrinsics are implemented for sin, cos, and log methods of java.lang.Math.

A No-Op Garbage Collector

Java 11 新增了一个叫做 Epsilon 的垃圾回收器。

之所以称为No-op无操作是因为它分配内存但不收集任何垃圾。因此,Epsilon可用于模拟内存溢出错误。
显然,Epsilon不适应于典型的生产Java应用程序,但有一些具体的场景下是非常有用的:

  • 性能测试
  • 内存压力测试
  • VM 接口测试
  • 运行时间极其短的任务

可以使用 -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC 启动。

Flight Recorder

Java Flight Recorder(JFR)目前在Open JDK中开源,之前一直作为商业特性只在 Oracle JDK 中提供 。
JFR是一款分析用具,从运行的Java程序中收集诊断信息和分析数据。

开启120s的JFR记录,可以用下面的参数:

1
-XX:StartFlightRecording=duration=120s,settings=profile,filename=java-demo-app.jfr

我们可以在生产环境中使用 JFR ,因为其性能开销通常低于 1% 。当记录时间到期之后,就可以得到一个存有记录的JFR文件,但是如果要进行分析或者查看其中的数据,
必须使用另一个叫做 JDK Mission Control (JMC) 的工具。

ZGC

ZGC 是一个全新的垃圾回收器,它旨在

  • GC 暂停时间不超过10ms
  • 适用于非常小(8MB)或者非常大(4TB)的堆内存
  • 与G1相比,应用吞吐量减少不超过15%
  • 为未来GC的特性与优化染色指针、读屏障奠定基础
  • 开始支持 Linux/x64 平台

后续 JDK 15 正式启用。ZGC确实是Java的最前沿的技术,但在当前 G1 都没有普及的时代( 我们现在大多也还是在用 CMS ),暂不探讨 ZGC

移除的和不推荐使用的模块

Java EE 和 CORBA

JEP 320

标准版本的 Java EE 技术已经在三方中可以获得, Java SE 中不需要在包含它了。
Java 9 中就已经把 Java EE and CORBA 置为 deprecated 了, Java 11中彻底删除了这些内容:

  • Java API for XML-Based Web Services (java.xml.ws)
  • Java Architecture for XML Binding (java.xml.bind)
  • JavaBeans Activation Framework (java.activation)
  • Common Annotations (java.xml.ws.annotation)
  • Common Object Request Broker Architecture (java.corba)
  • JavaTransaction API (java.transaction)

JMC and JavaFX

JDK中不再包含JMC(JDK Mission Control),目前可以下载JMC的独立版本。

JavaFX 模块同上。

不推荐使用的模块

其他改变