Java12新特性
switch表达式(预览)
传统switch的缺点
- 匹配是自上而下的,如果忘记写break, 后面的case语句不论匹配与否都会执行;
- 所有的case语句共用一个块范围,在不同的case语句定义的变量名不能重复;
- 不能在一个case里写多个执行结果一致的条件;
- 整个switch不能作为表达式返回值;
预览语言
switch 表达式是作为预览语言功能的第一个语言改动被引入新版 Java 中来的,预览语言功能的想法是在 2018 年 初被引入 Java 中的,本质上讲,这是一种引入新特性的测试版的方法。通过这种方式,能够根据用户反馈进行升级、更改,在极端情况下,如果没有被很好的接纳,则可以完全删除该功能。预览功能的关键在于它们没有被包含在 Java SE 规范中
语法详解
扩展的 switch 语句,不仅可以作为语句(statement),还可以作为表达式(expression),并且两种写法都可以使用传统的 switch 语法,或者使用简化的“case L ->”模式匹配语法作用于不同范围并控制执行流。这些更改将简化日常编码工作,并为 switch 中的模式匹配(JEP 305)做好准备
- 使用 Java 12 中 switch 表达式的写法,省去了 break 语句,避免了因少写 break 而出错。
- 同时将多个 case 合并到一行,显得简洁、清晰也更加优雅的表达逻辑分支,其具体写法就是将之前的 case 语 句表成了:case L ->,即如果条件匹配 case L,则执行标签右侧的代码 ,同时标签右侧的代码段只能是表达式、代码块或 throw 语句。
- 为了保持兼容性,case 条件语句中依然可以使用字符 : ,这时 fall-through 规则依然有效的,即不能省略原有 的 break 语句,但是同一个 Switch 结构里不能混用 -> 和 : ,否则会有编译错误。并且简化后的 switch 代码块中定义的局部变量,其作用域就限制在代码块中,而不是蔓延到整个 Switch 结构,也不用根据不同的判断条件来给变量赋值
使用详解
/**
* 之前的写法,switch 语句如果漏写了一个 break,那么逻辑往往就跑偏了,这种方式既繁琐,又容易出错
*/
public class SwitchTest {
public static void main(String[] args) {
int numberOfLetters;
Fruit fruit = Fruit.APPLE;
switch (fruit) {
case PEAR:
numberOfLetters = 4;
break;
case APPLE:
case GRAPE:
case MANGO:
numberOfLetters = 5;
break;
case ORANGE:
case PAPAYA:
numberOfLetters = 6;
break;
default:
throw new IllegalStateException("No Such Fruit:" + fruit);
}
System.out.println(numberOfLetters);
} }
enum Fruit {
PEAR, APPLE, GRAPE, MANGO, ORANGE, PAPAYA;
}
/**
* Java12中的switch 表达式
*/
public class SwitchTest1 {
public static void main(String[] args) {
Fruit fruit = Fruit.GRAPE;
// Pattern Matching 机制能够自然地保证只有单一路径会被执行
switch(fruit){
case PEAR -> System.out.println(4);
case APPLE,MANGO,GRAPE -> System.out.println(5);
case ORANGE,PAPAYA -> System.out.println(6);
default -> throw new IllegalStateException("No Such Fruit:" + fruit);
};
// 更进一步,下面的表达式,为我们提供了优雅地表达特定场合计算逻辑的方式
int numberOfLetters = switch(fruit){
case PEAR -> 4;
case APPLE,MANGO,GRAPE -> 5;
case ORANGE,PAPAYA -> 6;
default -> throw new IllegalStateException("No Such Fruit:" + fruit);
};
System.out.println(numberOfLetters);
}
}
Shenandoah GC:低停顿时间的GC(预览)
Shenandoah 垃圾回收器是 Red Hat 在 2014 年宣布进行的一项垃圾收集器研究项目 Pauseless GC 的实现,旨在针对JVM 上的内存收回实现低停顿的需求。该设计将与应用程序线程并发,通过交换 CPU 并发周期和空间以改善停顿时间,使得垃圾回收器执行线程能够在 Java 线程运行时进行堆压缩,并且标记和整理能够同时进行,因此避免了在 大多数 JVM 垃圾收集器中所遇到的问题。
据 Red Hat 研发 Shenandoah 团队对外宣称,Shenandoah 垃圾回收器的暂停时间与堆大小无关,这意味着无论将 堆设置为 200 MB 还是 200 GB,都将拥有一致的系统暂停时间,不过实际使用性能将取决于实际工作堆的大小和工 作负载。
与其他 Pauseless GC 类似,Shenandoah GC 主要目标是 99.9% 的暂停小于 10ms,暂停与堆大小无关等。 这是一个实验性功能,不包含在默认(Oracle)的OpenJDK版本中。
//推荐调试 Shenandoah 的 JVM 参数
-XX:+AlwaysPreTouch:使用所有可用的内存分页,减少系统运行停顿,为避免运行时性能损失。
-Xmx == -Xmsv:设置初始堆大小与最大值一致,可以减轻伸缩堆大小带来的压力,与 AlwaysPreTouch 参数配合使用,在启动时提交所有内存,避免在最终使用中出现系统停顿。
-XX:+ UseTransparentHugePages:能够大大提高大堆的性能,同时建议在 Linux 上使用时将 /sys/kernel/mm/transparent_hugepage/enabled 和 /sys/kernel/mm/transparent_hugepage/defragv设置为:madvise,同时与 AlwaysPreTouch 一起使 用时,init 和 shutdownv 速度会更快,因为它将使用更大的页面进行预处理。
-XX:+UseNUMA:虽然 Shenandoah 尚未明确支持 NUMA(Non-Uniform Memory Access),但最好启用此功 能以在多插槽主机上启用 NUMA 交错。与 AlwaysPreTouch 相结合,它提供了比默认配置更好的性能。
-XX:+DisableExplicitGC:忽略代码中的 System.gc() 调用。当用户在代码中调用 System.gc() 时会强制 Shenandoah 执行 STW Full GC ,应禁用它以防止执行此操作,
另外还可以使用 - XX:+ExplicitGCInvokesConcurrent,在 调用 System.gc() 时执行 CMS GC 而不是 Full GC,建议在有 System.gc() 调用的情况下使用。
不过目前 Shenandoah 垃圾回收器还被标记为实验项目,如果要使用Shenandoah GC需要编译时--with-jvm- features选项带有shenandoahgc,然后启动时使用参数
-XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC
JVM 常量 API
Java 12 中引入 JVM 常量 API,用来更容易地对关键类文件 (key class-file) 和运行时构件(artefact)的名义描述 (nominal description) 进行建模,特别是对那些从常量池加载的常量,这是一项非常技术性的变化,能够以更简单、标准的方式处理可加载常量。
具体来说就是java.base模块新增了java.lang.constant包(而非 java.lang.invoke.constant)。包中定义了一系列基于值的符号引用(JVMS 5.1)类型,它们能够描述每种可加载常量。
官方文档:Java SE > Java SE Specifications > Java Virtual Machine Specification下的第5章: Chapter 5. Loading, Linking, and Initializing
https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-5.html
引入了ConstantDesc接口( )以及Constable接口;
ConstantDesc接口定义了resolveConstantDesc方法,Constable接口定义了describeConstable方法;
String、Integer、Long、Float、Double均实现了这两个接口,EnumDesc实现了 ConstantDesc接口
符号引用以纯 nominal 形式描述可加载常量,与类加载或可访问性上下文区分开。有些类可以作为自己的符号引用 (例如 String)。而对于可链接常量,另外定义了一系列符号引用类型,具体包括: ClassDesc (Class 的可加载常量 标称描述符) ,MethodTypeDesc(方法类型常量标称描述符) ,MethodHandleDesc (方法句柄常量标称描述符) 和 DynamicConstantDesc (动态常量标称描述符) ,它们包含描述这些常量的 nominal 信息。此 API 对于操作类和方法的工具很有帮助