Pattern Matching for instanceof (Preview) (instanceof的模式匹配预览版)
JDK14之前的写法
Object obj = "kuaidi100";
if(obj instanceof String){
String str = (String) obj;
}
JDK14后可以这样写
Object obj = "kuaidi100";
if(obj instanceof String str){
//直接使用str
}
if (obj instanceof String s) {
// can use s here
} else {
// can't use s here
}
Switch Expressions (Switch表达式)
经过JDK12,13的迭代,最后在JDK14中将这个特性转正。
T result = switch (arg) {
case L1 -> e1;
case L2 -> e2;
default -> e3;
};
int result = switch (s) {
case "Foo":
yield 1;
case "Bar":
yield 2;
default:
System.out.println("Neither Foo nor Bar, hmmm...");
yield 0;
};
JFR Event Streaming JFR事件流
这个特性是JDK11 飞行记录器的升级功能,在JDK11中JFR只能将运行的数据导出文件,然后通过JMC可视化,这个过程太繁琐也不能应用于实时的监控,所以在JDK14中推出了JFR事件流。通过这个功能可以实时获取到JVM的运行情况。
Non-Volatile Mapped Byte Buffers 非易失性映射的字节缓冲
新的特定于jdk的文件映射模式,以便可以使用FileChannel API创建引用非易失性内存的MappedByteBuffer实例。在non-vllatile的情况下,如果像RAM一样关闭电源,数据也不会保留。唯一需要更改的API是FileChannel客户端使用的新枚举,用于请求位于NVM支持的文件系统(而不是传统的文件存储系统)上的文件的映射。
Helpful NullPointerExceptions 有用的NPE
在之前抛出NPE异常时只会提示哪一行出错了,但是那一行有多个操作,无法分辨出那个是null。比如下面的例子,不知道是a,还是b,还是c是null。
a.b.c.i = 99;
在JDK14前只是会提示哪一行有错误。
Exception in thread "main" java.lang.NullPointerException
at Prog.main(Prog.java:5)
在JDK14中就会明确的提示是那个字段为空。
Exception in thread "main" java.lang.NullPointerException:
Cannot assign field "i" because "a" is null
at Prog.main(Prog.java:5)
不过这个功能默认是关闭的,需要使用-XX:+ShowCodeDetailsInExceptionMessages来开启。之所以不默认打开是出于性能,安全,兼容性三个方面的考虑。
Records (Preview)记录类型预览版
Java的语法太过繁琐一直被吐槽,想要创建纯数据载体类通常都必须编写大量低价值、重复的、容易出错的代码。如:构造函数、getter/setter、equals()、hashCode()以及toString()等。很多人会选择使用IDE或者Lombok来生成这些代码,但是使用这些工具有时会有意想不到的结果,甚至引发生产BUG。所以在JDK14推出了一个纯数据载体的类型——record。
下面是一个record例子:
public record Kuaidi100Record(@Name("name") int x, int y) {
public static String one;
}
生成的class文件反编译后:
public record Kuaidi100Record(int x, int y) {
public static String one;
public Kuaidi100Record(int x, int y) {
this.x = x;
this.y = y;
}
@Name("name")
public int x() {
return this.x;
}
public int y() {
return this.y;
}
}
javap查看对应的汇编指令:
public final class feature_record.Kuaidi100Record extends java.lang.Record {
public static java.lang.String one;
public feature_record.Kuaidi100Record(int, int);
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Record."<init>":()V
4: aload_0
5: iload_1
6: putfield #7 // Field x:I
9: aload_0
10: iload_2
11: putfield #13 // Field y:I
14: return
public final java.lang.String toString();
Code:
0: aload_0
1: invokedynamic #16, 0 // InvokeDynamic #0:toString:(Lfeature_record/Kuaidi100Record;)Ljava/lang/String;
6: areturn
public final int hashCode();
Code:
0: aload_0
1: invokedynamic #20, 0 // InvokeDynamic #0:hashCode:(Lfeature_record/Kuaidi100Record;)I
6: ireturn
public final boolean equals(java.lang.Object);
Code:
0: aload_0
1: aload_1
2: invokedynamic #24, 0 // InvokeDynamic #0:equals:(Lfeature_record/Kuaidi100Record;Ljava/lang/Object;)Z
7: ireturn
public int x();
Code:
0: aload_0
1: getfield #7 // Field x:I
4: ireturn
public int y();
Code:
0: aload_0
1: getfield #13 // Field y:I
4: ireturn
}
使用
public static void main(String[] args) {
Kuaidi100Record kuaidi100Record = new Kuaidi100Record(1, 2);
System.out.println(kuaidi100Record);
}
通过上面的class文件与javap生成的汇编代码可以看到record做了下面几件事情:
- 没有指定构造器,Record 类会自动生成一个以所有属性为参数并且给每个属性赋值的构造器
- 生成成员变量的get方法,没有set方法。
get方法生成规则:
-
-
- 方法名就是属性名称
- 返回类型就是对应的属性类型
- 是一个 public 方法,并且没有声明抛出任何异常
- 方法体就是返回对应属性
- 如果属性上面有任何注解,那么这个注解如果能加到方法上那么也会自动加到这个方法上
-
3. 重写equals,hashCode,toString等方法。
-
-
- hashcode() 在编译的时候自动生成字节码实现,核心逻辑基于 ObjectMethods 的 makeHashCode 方法,里面的逻辑是对于每一个属性的哈希值移位组合起来。注意这里的所有调用(包括对于 ObjectMethods 的方法调用以及获取每个属性)都是利用 MethodHandle 实现的近似于直接调用的方式调用的。
- equals() 在编译的时候自动生成字节码实现,核心逻辑基于 ObjectMethods 的 makeEquals 方法,里面的逻辑是对于两个 Record 对象每一个属性判断是否相等(对于引用类型用Objects.equals(),原始类型使用 ==),注意这里的所有调用(包括对于 ObjectMethods 的方法调用以及获取每个属性)都是利用 MethodHandle 实现的近似于直接调用的方式调用的。
- toString() 在编译的时候自动生成字节码实现,核心逻辑基于 ObjectMethods 的 makeToString 方法,里面的逻辑是对于每一个属性的值组合起来构成字符串。注意这里的所有调用(包括对于 ObjectMethods 的方法调用以及获取每个属性)都是利用 MethodHandle 实现的近似于直接调用的方式调用的。
-
4.record默认为final的,成员变量也为final,所以没有set方法。
record特性在JDK14还只是预览版,在JDK15中对它进行了继续加强。
Foreign-Memory Access API (Incubator)外部存储器访问特性孵化
引入API以允许Java程序安全有效地访问Java堆之外的外部存储器。实现了一种提供“通用性”,“安全性”和“确定性”的“外部存储器API,用来替换 java.nio.ByteBuffer和sun.misc.Unsafe。
JDK引入这个特性的动机是现在有许多基于Java的库和应用都使用了堆外内存访问,比如mapDB,netty的ByteBuf,memcached等。使用堆外内存的动机:
- 避免GC不可预测和性能损耗
- 多个进程之间共享内存
- 通过将文件映射到内存中(例如,通过mmap)来序列化和反序列化内存内容
但是在JDK14之前JDK并没有提供很好的堆外内存访问的API
在1.4引进的ButeBuffer允许创建direct类型的byte来进行堆外内存操作,但是它处理的内存不能超过2G。只有当垃圾收集器认为直接缓冲区不可达时,关联的内存才会被释放。此外,使用直接缓冲区可能很麻烦,因为与直接缓冲区关联的内存的回收工作留给了垃圾收集器。除了ByteBuffer外还可以使用Unsafe来进行堆外内存访问,但是由于它允许访问任何内存位置(例如,不安全::gettint需要一个长地址)。这使得Java程序有可能使JVM崩溃。
为此在JDK14引入了MemorySegment, MemoryAddress and MemoryLayout。
其他特性
移除CMS垃圾收集器
ZGC支持windows,macOS
弃用ParallelScavenge + SerialOld GC组合
Text Blocks (Second Preview):这个在JDK13中推出,在JDK14中继续对其进行了扩展。
Packaging Tool (Incubator):一个打包工具,可以将java应用直接打包成rpm,dmg或者exe在各自平台可以点击直接运行。
NUMA-Aware Memory Allocation for G1:通过实现NUMA-ware(非统一内存访问)内存分配提升G1在大型机的性能。
完整特性