Hello World

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

0%

Java 10新特性

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

baeldung New Features in Java 10

JDK 10 的新特性

  • 286: Local-Variable Type Inference (局部变量类型推断)
  • 296: Consolidate the JDK Forest into a Single Repository (JDK库的合并)
  • 304: Garbage-Collector Interface (统一的垃圾回收接口)
  • 307: Parallel Full GC for G1 (G1并行Full GC)
  • 310: Application Class-Data Sharing (应用程序类数据共享)
  • 312: Thread-Local Handshakes (ThreadLocal握手交互)
  • 313: Remove the Native-Header Generation Tool (javah) (移除JDK中附带的javah工具)
  • 314: Additional Unicode Language-Tag Extensions (附加的Unicode语言标记扩展)
  • 316: Heap Allocation on Alternative Memory Devices
  • 317: Experimental Java-Based JIT Compiler (实验性的基于 Java 的 JIT 编译器)
  • 319: Root Certificates (根证书)
  • 322: Time-Based Release Versioning (基于时间的发布版本)

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

局部变量类型推断(var)

JEP 286

Local Variable Type Inference Style Guidelines

Java 10 LocalVariable Type-Inference

Java 10 采用了一个叫做 var保留类型来实现局部变量推断。要特别注意的是,为了兼容旧版本,var 不是关键字,而是一个保留类型,也就意味着你仍然可以像这样用 var 为你的变量和函数命名:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int var = 10;  
```

exp:
```java
var id = 0;
var codefx = new URL("https://mp.weixin.qq.com/");
var list = new ArrayList<>();
var list = List.of(1, 2, 3);
var map = new HashMap<String, String>();
var p = Paths.of("src/test/java/Java9FeaturesTest.java");
var numbers = List.of("a", "b", "c");
for (var n : list) System.out.print(n+ " ");
try (var is = Files.newInputStream(p)) {} // type of 'is' is InputStream

请注意,var 的作用仅仅是推断变量类型,变量仍然是静态类型的,与 JS 中的 var 作用完全不同。下面这段代码无法通过编译:

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
var i = 10;  
i = "20"; // error: incompatible types
```

使用 var 声明的变量时必须要在声明的同时初始化,所以下面这段代码是无法编译的:
```java
var a; // error: 'var' on variable without initializer
a = 10;
```

同时,var 也不能用于**局部变量**声明以外的地方(唯一的例外是 `Java 11` 允许在 [lambda 表达式的形式参数中使用 var 语法](http://openjdk.java.net/jeps/323)):
```java
class C {
var i = 10; // error: 'var' is not allowed here
var f1() { // error: 'var' is not allowed here
return 10;
}
void f2(var str) { // error: 'var' is not allowed here
try {
// ...
} catch (var e) { // error: 'var' is not allowed here
// ...
}
System.out.println(str);
}
}

var 还能用来声明一些不可指示类型(Non-denotable types)的变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var obj = new Object() {  // type of 'obj' is A anonymous class types  
int i = 10;
void f() {}
};

var list = (List<String> & AutoCloseable) null; // type of 'list' is List<String> & AutoCloseable
```

不过直接用 null 初始化 var 声明的变量是不合法:
```java
var a = null; // error: variable initializer is 'null'
```

以上还是**类型是否明确**的区别。

还有一点也要注意,_菱形推断_ 的时候推断出的类型可能在意料之外:
```java
var l = new ArrayList<>(); // type of 'l' is ArrayList<Object>

var用于声明不可指类型的变量特性的妙用

Java 8 的 lambda 表达式不能够捕获可变变量,也就是说下面这个代码是错误的:

1
2
3
4
int count = 0;
List.of("ice1000", "Glavo")
.forEach(e -> count += 1); //error
System.out.println(count);

之前想要绕过这个限制,我们可以用单元素的数组实现。而在 Java 10 中我们又多了一种选择:

1
2
3
4
5
6
var context = new Object() {
int count = 0;
}
List.of("ice1000", "Glavo")
.forEach(e -> context.count += 1);
System.out.println(context.count);

var 还能够帮助我们实现嵌套函数:

1
2
3
4
5
6
7
8
9
10
11
int factorial(int i) {
var context = new Object() {
int fact(int i, int accumulator) {
if (i <= 1)
return accumulator;
else
return fact(i - 1, i * accumulator);
}
};
return context.fact(i, 1);
}

语言特性

集合增强

ListSetMap 提供了静态方法copyOf()返回入参集合的一个不可变拷贝。

1
2
3
static <E> List<E> copyOf(Collection<? extends E> coll) {
return ImmutableCollections.listCopy(coll);
}

使用 copyOf() 创建的集合为不可变集合,不能进行添加、删除、替换、 排序等操作,不然会报 java.lang.UnsupportedOperationException 异常。

并且,java.util.stream.Collectors 中新增了静态方法,用于将流中的元素收集为不可变的集合。

1
2
3
var list = new ArrayList<>();
list.stream().collect(Collectors.toUnmodifiableList());
list.stream().collect(Collectors.toUnmodifiableSet());

Optional 增强

Optional 新增了orElseThrow()方法来在没有值时抛出指定的异常。

1
2
Optional.ofNullable(cache.getIfPresent(key))
.orElseThrow(() -> new PrestoException(NOT_FOUND, "Missing entry found for key: " + key));

Garbage-Collector Interface

垃圾收集器接口。

在 hotspot/gc 代码实现方面,引入一个干净的垃圾收集器接口,改进不同垃圾收集器源代码的隔离性。这样添加新的或者删除旧的 GC,都会更容易。

Parallel Full GC for G1

为了降低G1垃圾回收器最坏情况下的延迟,使Full GC的 mark-sweep-compact 算法并行执行。执行的线程可以通过-XX:ParallelGCThreads 设置,同时也受到 Young 和 Mixed 回收器线程的影响。 JEP 307: Parallel Full GC for G1

The G1 garbage collector is the default one since JDK 9. However, the full GC for G1 used a single threaded mark-sweep-compact algorithm.

This has been changed to the parallel mark-sweep-compact algorithm in Java 10 effectively reducing the stop-the-world time during full GC.

Application Class-Data Sharing

Java 之前( JDK 5 )就引入了类数据共享机制,Class data sharing (CDS) ,以减少 Java 程序的启动时间,降低内存占用。简单来说,Java 安装程序会把 rt.jar 中的核心类提前转化成内部表示,转储到一个共享的文件中(shared archive)。多个 Java 进程(或者说 JVM 实例)可以共享这部分数据。之前只允许 bootstrap class loader , jdk 10也允许app class loader自定义 classloader共享archived classes

java-10-performance-improvements

Remove the Native-Header Generation Tool

从 JDK 8 开始,javah 的功能已经集成到了 javac 中。所以,javah 可以删掉了。

Additional Unicode Language-Tag Extensions

Enhance java.util.Locale and related APIs to implement additional Unicode extensions of BCP 47 language tags.

Experimental Java-Based JIT Compiler

Graal 是一个基于 Java 语言编写的 JIT 编译器,是 JDK 9 中引入的实验性 Ahead-of-Time (AOT) 编译器的基础。

Oracle 的 HotSpot VM 便附带两个用 C++ 实现的 JIT compiler:C1 及 C2。在Java 10 (Linux/x64, macOS/x64) 中,默认情况下HotSpot 仍使用C2,但通过向java 命令添加
-XX:+UnlockExperimentalVMOptions -XX:+UseJVMCICompiler
参数便可将 C2 替换成 Graal。

PS. JIT

JIT(just in time 即时编译器),当虚拟机发现某个方法或代码块运行特别频繁时,就会把这些代码认定为 Hot Spot Code 热点代码,为了提高热点代码的执行效率,在运行时,虚拟机将会把这些代码编译成与本地平台相关的机器码,并进行各层次的优化。

即使开启了JIT,也少不了代码编译和字节码解释的过程。JIT处理的是热点代码(hotspot code)。热点代码就是频繁执行的代码块,比如循环里面的代码。JIT有一套逻辑判断是否热点代码。

既然JIT处理后的是机器能够快速执行的代码,为啥还要解释执行呢,干嘛不把全部代码编译成机器代码呢?这是由于编译本地代码比较费时间,而且编译后还要进行进一步的优化导致耗时更久;而解释器是能够立即解释字节码文件的,毕竟我们的应用放到服务器上的时候就已经是字节码文件了,解释器可以拿来直接用。而且解释器执行的时候占用的内存更小,在内存受限的场景难以使用编译器(比如手机上)。编译器会概率性地选择多数时候都能提升运行效率的手段进行优化,如果“优化”后发现还不如不优化(甚至执行有问题)就得“逆优化”,回退到解释执行状态。

我们可以通过最简单的查看Java版本的命令查看Java是否使用了编译器:

1
2
3
4
5
 > java -version

java version "1.8.0_191"
Java(TM) SE Runtime Environment (build 1.8.0_191-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.191-b12, mixed mode)

最后输出的mixed mode代表是混合模式,也就是先解释执行,并逐步将热点代码代替为机器代码。不使用编译器的模式叫interpreted mode;优先使用编译器的模式叫compiled modecompiled mode会优先采用编译方式执行程序,如果编译执行有问题就回退到解释执行。

Root Certificates

在 JDK 中提供一组默认的根证书。

Open-source the root certificates in Oracle’s Java SE Root CA program in order to make OpenJDK builds more attractive to developers, and to reduce the differences between those builds and Oracle JDK builds.

Time-Based Release Versioning

基于时间的版本字符串。修改 Java SE 平台和 JDK 版本字符串机制。

  1. A new Java release every six months. The March 2018 release is JDK 10, the September 2018 release is JDK 11, and so forth. These are called feature releases and are expected to contain at least one or two significant features
  2. Support for the feature release will last only for six months, i.e., until next feature release
  3. Long-term support release will be marked as LTS. Support for such release will be for three years
  4. Java 11 will be an LTS release

java -version will now contain the GA date, making it easier to identify how old the release is:

1
2
3
4
$ java -version 
openjdk 17.0.2 2022-01-18 LTS
OpenJDK Runtime Environment Zulu17.32+13-CA (build 17.0.2+8-LTS)
OpenJDK 64-Bit Server VM Zulu17.32+13-CA (build 17.0.2+8-LTS, mixed mode, sharing)

参考

Java 10 新特性概览

Java 10 LocalVariable Type-Inference

Java 10 新特性之局部变量类型推断