Java 语言的语法糖

本文最后更新于:1 年前

Reference

环境和工具

不能确定 JDK 版本的语法糖

+

SyntacticSugar\src\io\weichao\Plus.java

1
2
3
4
5
6
7
8
package io.weichao;

public class Plus {
public static void main(String[] args) {
byte b1 = (byte) 254;
System.out.println("foo " + b1 + ((byte) 254 > (byte) 1));
}
}

编译后生成:SyntacticSugar\bin\io\weichao\Plus.class

反编译使用的命令:

1
jad.exe -noctor -nonlb -o -p -space Plus.class

反编译后:

解语法糖:

  • 本质是使用了 StringBuilder。

Constant Folding 常量折叠

SyntacticSugar\src\io\weichao\ConstantFolding.java

1
2
3
4
5
6
7
8
9
10
package io.weichao;

public class ConstantFolding {
public static void main(String[] args) {
String s1 = "a" + "bc";
String s2 = "ab" + "c";
System.out.println(s1 == s2);
System.out.println(s1.equals(s2));
}
}

编译后生成:SyntacticSugar\bin\io\weichao\ConstantFolding.class

反编译使用的命令:

1
java -jar cfr.jar ConstantFolding.class

反编译后:

解语法糖:

  • 常量直接连接到了一起。

JDK Alpha

JDK Beta

JDK 1.0

JDK 1.1

Inner Classes 内部类

成员内部类

SyntacticSugar\src\io\weichao\InnerClasses.java

1
2
3
4
5
6
7
8
9
10
11
12
13
package io.weichao;

public class InnerClasses {
private class Printer {
private void print() {
System.out.print("hello");
}
}

public static void main(String[] args) {
new InnerClasses().new Printer().print();
}
}

编译后生成:SyntacticSugar\bin\io\weichao\InnerClasses.class
SyntacticSugar\bin\io\weichao\InnerClasses$Printer.class

反编译使用的命令:

1
java -jar cfr.jar InnerClasses.class --innerclasses true --removeinnerclasssynthetics false

反编译后:

解语法糖:

  • 内部类的构造方法的参数是外部类对象,所以内部类对象会隐式持有外部类对象的引用这会影响外部类对象的回收

局部内部类

定义在一个方法或者一个作用域里面的类,局部内部类的访问仅限于该方法内或者该作用域内

SyntacticSugar\src\io\weichao\InnerClasses2.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package io.weichao;

public class InnerClasses2 {
private void print() {
class Printer {
private void print() {
System.out.print("hello");
}
}
new Printer().print();
}

public static void main(String[] args) {
new InnerClasses2().print();
}
}

编译后生成:SyntacticSugar\bin\io\weichao\InnerClasses2.class
SyntacticSugar\bin\io\weichao\InnerClasses2$1$Printer.class

反编译使用的命令:

1
java -jar cfr.jar InnerClasses2.class --innerclasses true --removeinnerclasssynthetics false

反编译后:

解语法糖:

  • 内部类的构造方法的参数是外部类对象,所以内部类对象会隐式持有外部类对象的引用这会影响外部类对象的回收

匿名内部类

SyntacticSugar\src\io\weichao\InnerClasses3.java

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
package io.weichao;

interface IWorker {
void doWork();
}

class Printer {
public void print(IWorker worker) {
worker.doWork();
}
}

public class InnerClasses3 {
public void print() {
System.out.print("hello");
}

public void planToPrint() {
new Printer().print(new IWorker() {
@Override
public void doWork() {
print();
}
});
}

public static void main(String[] args) {
new InnerClasses3().print();
}
}

编译后生成:SyntacticSugar\bin\io\weichao\InnerClasses3.classSyntacticSugar\bin\io\weichao\InnerClasses3$1.classSyntacticSugar\bin\io\weichao\IWorker.class
SyntacticSugar\bin\io\weichao\Printer.class

反编译使用的命令:

1
2
3
java -jar cfr.jar InnerClasses3.class --innerclasses true --removeinnerclasssynthetics false

java -jar cfr.jar InnerClasses3$1.class --innerclasses true --removeinnerclasssynthetics false

反编译后:

解语法糖:

  • 内部类的构造方法的参数是外部类对象,所以内部类对象会隐式持有外部类对象的引用这会影响外部类对象的回收

静态内部类

使用 static 关键字修饰的内部类。

SyntacticSugar\src\io\weichao\InnerClasses4.java

1
2
3
4
5
6
7
8
9
10
11
12
13
package io.weichao;

public class InnerClasses4 {
static class Printer {
private void print() {
System.out.print("hello");
}
}

public static void main(String[] args) {
new Printer().print();
}
}

编译后生成:SyntacticSugar\bin\io\weichao\InnerClasses4.classSyntacticSugar\bin\io\weichao\InnerClasses4$Printer.class

反编译使用的命令:

1
java -jar cfr.jar InnerClasses4.class --innerclasses true --removeinnerclasssynthetics false

反编译后:

  • 静态内部类不需要依赖外部类。

问题

局部内部类匿名内部类只能访问外部类的局部 final 变量

SyntacticSugar\src\io\weichao\InnerClassesQ.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package io.weichao;

public class InnerClassesQ {
public void print(final String s1) {
final String s2 = "world";
new Thread() {
public void run() {
System.out.println(s1);
System.out.println(s2);
};
}.start();
}

public static void main(String[] args) {
new InnerClassesQ().print("hello");
}
}

正向分析:
【存在的问题】可能因为变量的生命周期导致变量不可访问。
【解决问题】复制变量:如果变量的值在编译期可以确定,就在常量池中生成一个值给内部类用;如果不能确定,就将变量的值传给内部类通过构造方法传参

  • 【引入问题】如果内部类修改了传进来的复制的值,无法同步给外部类原来的变量。
    【解决问题】将变量使用 final 修饰,不允许修改对于引用类型的变量,是不允许指向新的对象

编译后生成:SyntacticSugar\bin\io\weichao\InnerClassesQ.classSyntacticSugar\bin\io\weichao\InnerClassesQ$1.class

反编译使用的命令:

1
2
3
java -jar cfr.jar InnerClassesQ.class --innerclasses true --removeinnerclasssynthetics false

java -jar cfr.jar InnerClassesQ$1.class --innerclasses true --removeinnerclasssynthetics false

反编译后:

解语法糖:

  • 内部类的构造方法的参数第 1 个是外部类对象,所以内部类对象会隐式持有外部类对象的引用这会影响外部类对象的回收
  • 去掉了变量的 final 修饰符;对于确定的值 world 直接使用字面量,对于不确定的值 hello 使用参数第 2 个传递的值。

Serialization 序列化、持久化

  • Java 提供了一种对象序列化的机制:一个对象可以被表示为一个字节序列包括该对象的数据、对象类型和存储在该对象中的数据的类型
  • 反序列化:可以通过读取写入了序列化对象的文件,在内存中重建对象。
  • 序列化和反序列化可以跨平台完全由 Java 虚拟机实现。

SyntacticSugar\src\io\weichao\Printer.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package io.weichao;

public class Printer implements java.io.Serializable {
private static final long serialVersionUID = -4073265111741051800L;

private String text;
private transient String local;

public void setText(String text) {
this.text = text;
}

public String getText() {
return text;
}

public String getLocal() {
return local;
}

public void setLocal(String local) {
this.local = local;
}
}

SyntacticSugar\src\io\weichao\Serialization.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package io.weichao;

import java.io.*;

public class Serialization {
public static void main(String[] args) {
Printer printer = new Printer();
printer.setText("hello");
printer.setLocal("d:\\");

try {
FileOutputStream fos = new FileOutputStream("printer.ser");
ObjectOutputStream out = new ObjectOutputStream(fos);
out.writeObject(printer);
out.close();
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

使用 WinHex 打开 printer.ser:

序列化生成的文件格式[1]解析:

十六进制描述
0xACED魔数
0x0005版本号:5
0x73接下来读取到的将是一个对象
0x72该对象是一个对类的描述
0x0012长度:18
0x696F2E7765696368616F2E5072696E746572io.weichao.Printer
0xC778DAF642AECC68 = -4073265111741051800serialVersionUID 的值
0x02标识类版本:2
0x0001字段个数:1
0x4C类型是对象
0x0004长度:4
0x74657874text
0x74String 类型
0x0012长度:18
0x4C类型是对象
0x6A6176612F6C616E672F537472696E673B78java/lang/String;
0x78结束
0x70没有超类
0x74String 类型
0x0005长度:5
0x68656C6C6Fhello

SyntacticSugar\src\io\weichao\Deserialization.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package io.weichao;

import java.io.*;

public class Deserialization {
public static void main(String[] args) {
Printer printer = null;

try {
FileInputStream fis = new FileInputStream("printer.ser");
ObjectInputStream in = new ObjectInputStream(fis);
printer = (Printer) in.readObject();
in.close();
fis.close();
} catch (Exception e) {
e.printStackTrace();
return;
}
System.out.println(printer.getText()); // hello
System.out.println(printer.getLocal()); // null
}
}

编译后生成:SyntacticSugar\bin\io\weichao\Printer.classSyntacticSugar\bin\io\weichao\Serialization.classSyntacticSugar\bin\io\weichao\Deserialization.class

TODO 反编译后和 java 文件基本一致,所以序列化应该不算一个语法糖?

通过反编译可以看到:

  • transient 修饰的变量不会被序列化。
  • 类属性的 setter() 和 getter() 方法对序列化没有影响,也不在序列化文件中体现。

J2SE 1.2

SUN DELIVERS NEXT VERSION OF THE JAVA PLATFORM —— Java™ 2 Brand Unveiled:
https://web.archive.org/web/20070816170028/http://www.sun.com/smi/Press/sunflash/1998-12/sunflash.981208.9.xml

strictfp keyword strictfp 关键字

by JVM 17 an obsolete keyword, shouldn’t be used in new code

J2SE 1.3

Java 2 SDK, Standard Edition, version 1.3 —— Summary of New Features and Enhancements:
https://web.archive.org/web/20061107051825/https://java.sun.com/j2se/1.3/docs/relnotes/features.html

J2SE 1.4

JavaTM 2 SDK, Standard Edition, version 1.4.2 —— Summary of New Features and Enhancements:
https://web.archive.org/web/20070108015942/https://java.sun.com/j2se/1.4.2/docs/relnotes/features.html

Assertion Facility 断言

  • 可以快速揭示程序员对程序行为的误解。
  • Eclipse 开启断言:添加 VM arguments -ea

SyntacticSugar\src\io\weichao\Assertion.java

1
2
3
4
5
6
7
8
9
10
package io.weichao;

public class Assertion {
public static void main(String[] args) {
int a = 1;
int b = 2;

assert a == b : "a != b";
}
}

编译后生成:SyntacticSugar\bin\io\weichao\Assertion.class

反编译使用的命令:

1
java -jar cfr.jar Assertion.class --sugarasserts false

反编译后:

解语法糖:

  • 本质是 if 语句。如果断言结果为 true,则什么都不做,程序继续执行;如果断言结果为 false,则程序抛出 AssertError 来打断程序的执行。

Java SE 5

New Features and Enhancements —— J2SE 5.0:
https://docs.oracle.com/javase/1.5.0/docs/relnotes/features.html

Generics 泛型

  • 允许类型或方法对各种类型的对象进行操作,同时提供了编译时类型安全检测机制该机制允许程序员在编译时检测到非法的类型
  • 为集合框架添加了编译时类型安全性,且有自动装箱/拆箱。
  • 泛型的本质是参数化类型所操作的数据类型被指定为一个参数

泛型方法

1、无界的类型参数
SyntacticSugar\src\io\weichao\Generics.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package io.weichao;

public class Generics {
public <E> void print(E[] array) {
for (E element : array) {
System.out.printf("%s ", element);
}
System.out.println();
}

public static void main(String args[]) {
String[] array = { "hello", "world" };
new Generics().print(array); // hello world

Integer[] array2 = { 1, 2, 3 };
new Generics().print(array2); // 1 2 3
}
}

编译后生成:SyntacticSugar\src\io\weichao\Generics.class

反编译使用的命令:

1
jad.exe -noctor -nonlb -o -p -space Generics.class

反编译后:

解语法糖:

  • 【类型擦除】方法参数的类型被替换为 Object

2、有界的被允许传递到一个类型参数的类型种类范围类型参数
SyntacticSugar\src\io\weichao\Generics2.java

1
2
3
4
5
6
7
8
9
10
11
12
13
package io.weichao;

public class Generics2 {
public <T extends Comparable<T>> void print(T t1, T t2) {
System.out.printf("%s.compareTo(%s): %s", t1, t2, t1.compareTo(t2));
System.out.println();
}

public static void main(String args[]) {
new Generics2().print("hello", "world"); // hello.compareTo(world): -15
new Generics2().print(1, 2); // 1.compareTo(2): -1
}
}

编译后生成:SyntacticSugar\src\io\weichao\Generics2.class

反编译使用的命令:

1
jad.exe -noctor -nonlb -o -p -space Generics2.class

反编译后:

解语法糖:

  • 【类型擦除】方法参数的类型被替换为上界 Comparable

泛型类

SyntacticSugar\src\io\weichao\Generics3.java

1
2
3
4
5
6
7
8
9
10
11
package io.weichao;

public class Generics3<T> {
public void print(T t) {
System.out.print(t);
}

public static void main(String args[]) {
new Generics3<String>().print("hello");
}
}

编译后生成:SyntacticSugar\src\io\weichao\Generics3.class

反编译使用的命令:

1
jad.exe -noctor -nonlb -o -p -space Generics3.class

反编译后:

解语法糖:

  • 【类型擦除】方法参数的类型被替换为 Object

Enhanced for Loop foreach、增强 for 循环

消除了迭代器索引变量在迭代集合和数组时的繁琐和易错性。

SyntacticSugar\src\io\weichao\EnhancedForLoop.java

1
2
3
4
5
6
7
8
9
10
package io.weichao;

public class EnhancedForLoop {
public static void main(String[] args) {
String[] array = { "hello", "world" };
for (String s : array) {
System.out.println(s);
}
}
}

编译后生成:SyntacticSugar\src\io\weichao\EnhancedForLoop.class

反编译使用的命令:

1
jad.exe -noctor -nonlb -o -p -space EnhancedForLoop.class

反编译后:

解语法糖:

  • 本质是普通的 for 循环。

Autoboxing/Unboxing 自动装箱/拆箱

不需要手动转换原始类型如 int包装类型如 Integer

SyntacticSugar\src\io\weichao\Autoboxing.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package io.weichao;

import java.util.ArrayList;
import java.util.List;

public class Autoboxing {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();

list.add(1);
System.out.println(list);

int a = list.get(0);
System.out.println(a);
}
}

编译后生成:SyntacticSugar\bin\io\weichao\Autoboxing.class

反编译使用的命令:

1
jad.exe -noctor -nonlb -o -p -space Autoboxing.class

反编译后:

解语法糖:

  • int 被自动装箱成 Integer,Integer 被自动拆箱成 int。

Typesafe Enums 枚举

创建具有任意方法和字段的枚举类型。

SyntacticSugar\src\io\weichao\Enums.java

1
2
3
4
5
package io.weichao;

enum Color {
RED, GREEN, BLUE;
}

编译后生成:SyntacticSugar\bin\io\weichao\Color.class

反编译使用的命令:

1
jad.exe -noctor -nonlb -o -p -space Color.class

反编译后:

解语法糖:

  • 本质是一个继承 Enum 的类,修饰符是 final。
  • 枚举元素的修饰符是 public static final。
  • 创建了一个数组 ENUM$VALUES[],修饰符是 private static final。
  • 构造方法有两个参数:String 类型的枚举元素名、int 类型的索引。
  • 在静态代码块中,通过构造方法给枚举元素设置索引,并加入数组 ENUM$VALUES[]
  • 当调用 values() 方法时,会使用 System.arraycopy() 方法复制 ENUM$VALUES[] 到一个新数组,并返回新数组。
  • 当调用 valueOf() 方法时,会调用 Enum.valueOf() 方法返回枚举元素。

Varargs 可变参数、变长参数

不需要手动将参数列表装箱到数组中。

SyntacticSugar\src\io\weichao\Varargs.java

1
2
3
4
5
6
7
package io.weichao;

public class Varargs {
private void func(String... args) {
System.out.print(args);
}
}

编译后生成:SyntacticSugar\bin\io\weichao\Varargs.class

反编译使用的命令:

1
jad.exe -noctor -nonlb -o -p -space Varargs.class

反编译后:

解语法糖:

  • 本质是数组。

Static Import 静态导入

不需要使用类名限定静态成员。

Annotations(Metadata) 注解(元数据)

使用工具从源代码中的注解生成样板代码声明式编程风格

SyntacticSugar\src\io\weichao\Annotations.java

1
2
3
4
package io.weichao;

public @interface Annotations {
}

编译后生成:SyntacticSugar\bin\io\weichao\Annotations.class

反编译使用的命令:

1
jad.exe -noctor -nonlb -o -p -space Annotations.class

反编译后:

解语法糖:

  • 本质是一个继承 Annotation 的接口。

Java SE 6

Java SE 7 (LTS 2011.07~2019.07、2022.07)

Binary Literals 二进制字面量

整数类型 byte、short、int 和 long 也可以使用二进制数字表示。

SyntacticSugar\src\io\weichao\BinaryLiterals.java

1
2
3
4
5
package io.weichao;

public class BinaryLiterals {
int mask = 0b0011;
}

编译后生成:SyntacticSugar\bin\io\weichao\BinaryLiterals.class

反编译使用的命令:

1
jad.exe -noctor -nonlb -o -p -space BinaryLiterals.class

反编译后:

解语法糖:

  • 二进制转成了十进制。

Underscores in Numeric Literals 数字字面量中的下划线

任意数量的 _ 可以出现在数字字面量中数字之间的任何位置。

SyntacticSugar\src\io\weichao\UnderscoresInNumericLiterals.java

1
2
3
4
5
package io.weichao;

public class UnderscoresInNumericLiterals {
int million = 1_000_000;
}

编译后生成:SyntacticSugar\bin\io\weichao\UnderscoresInNumericLiterals.class

反编译使用的命令:

1
jad.exe -noctor -nonlb -o -p -space -radix 10 UnderscoresInNumericLiterals.class

反编译后:

解语法糖:

  • 去掉了下划线。

Strings in switch Statements switch 语句中的字符串

可以在 switch 语句的表达式中使用 String 类。

SyntacticSugar\src\io\weichao\StringsInSwitchStatements.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package io.weichao;

public class StringsInSwitchStatements {
public static void main(String[] args) {
switch ("hello") {
case "hello":
System.out.println("hello");
break;
case "world":
System.out.println("world");
break;
default:
break;
}
}
}

编译后生成:SyntacticSugar\bin\io\weichao\StringsInSwitchStatements.class

反编译使用的命令:

1
jad.exe -noctor -nonlb -o -p -space StringsInSwitchStatements.class

反编译后:

解语法糖:

  • 先使用 hashCode() 方法计算字符串的哈希值,通过哈希值查找 case,再使用 equals() 方法比较避免哈希碰撞导致的错误

Type Inference for Generic Instance Creation 泛型实例创建的类型推断

使用 <> 替换调用泛型类的构造函数所需的类型参数。

Improved Compiler Warnings and Errors When Using Non-Reifiable Formal Parameters with Varargs Methods

使用编译器选项 -Xlint:varargs 以及注释 @SafeVarargs@SuppressWarnings({"unchecked", "varargs"}) 可以关闭编译器的警告。

The try-with-resources Statement try-with-resources 语句

在语句结束时会自动关闭资源任何实现新的 java.lang.AutoCloseable 接口或 java.io.Closeable 接口的对象都可以用作资源

SyntacticSugar\src\io\weichao\TryWithResources.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package io.weichao;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class TryWithResources {
public static void main(String[] args) {
try (BufferedReader br = new BufferedReader(new FileReader("test.txt"))) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

编译后生成:SyntacticSugar\bin\io\weichao\TryWithResources.class

反编译使用的命令:

1
java -jar cfr.jar TryWithResources.class --tryresources false

反编译后:

解语法糖:

  • 添加了 finally 代码块,关闭缓冲流。

Catching Multiple Exception Types and Rethrowing Exceptions with Improved Type Checking 捕获多种异常类型并通过改进的类型检查重新抛出异常

  • 单个 catch 块可以处理不止一种类型的异常。
  • 可以在方法声明的 throws 子句中指定更具体的异常类型。

Java SE 8 (LTS 2014.03~2022.03、2030.12)

Lambda Expressions Lambda 表达式、闭包[2][3]

允许把一个方法作为另一个方法的参数,而不需要一个含有方法的类的对象一个含有方法的接口实现类的对象

Method references 方法引用

SyntacticSugar\src\io\weichao\LambdaExpressions.java

1
2
3
4
5
6
7
8
9
10
11
package io.weichao;

import java.util.Arrays;
import java.util.List;

public class LambdaExpressions {
public static void main(String[] args) {
List<String> list = Arrays.asList("hello", "world");
list.forEach(System.out::println);
}
}

编译后生成:SyntacticSugar\bin\io\weichao\LambdaExpressions.class

反编译使用的命令:

1
java -jar cfr.jar LambdaExpressions.class --decodelambdas false

反编译后:

解语法糖:

  • 本质是调用 LambdaMetafactory.metafactory() 方法。

Default methods 默认方法

接口允许拥有带有方法实现的默认方法,在方法前使用关键字 default 来区别于普通的抽象方法。

New and Enhanced APIs That Take Advantage of Lambda Expressions and Streams in Java SE 8

新的和增强的 APIs——借助 Java SE 8 中的 Lambda 表达式和流。

Improved Type Inference

可以利用目标类型来推断泛型方法调用的类型参数。

Annotations on Java Types

可以在任何使用类型的地方应用注解。

Repeating Annotations 重复注解

允许在同一声明类型(类,属性,或方法)上多次使用同一个注解。

SyntacticSugar\src\io\weichao\FieldMap.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package io.weichao;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(FieldMaps.class)
@Documented
public @interface FieldMap {
String channel() default "";

String value();
}

SyntacticSugar\src\io\weichao\FieldMaps.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package io.weichao;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FieldMaps {
FieldMap[] value();
}

SyntacticSugar\src\io\weichao\RepeatingAnnotations.java

1
2
3
4
5
6
7
package io.weichao;

public class RepeatingAnnotations {
@FieldMap(channel = "class", value = "10")
@FieldMap(channel = "school", value = "20220110")
private String studentNumber;
}

编译后生成:SyntacticSugar\bin\io\weichao\FieldMap.classSyntacticSugar\bin\io\weichao\FieldMaps.classSyntacticSugar\bin\io\weichao\RepeatingAnnotations.class

反编译使用的命令:

1
java -jar cfr.jar RepeatingAnnotations.class

反编译后:

解语法糖:

  • 对于重复注解,表面上使用的是 FieldMap,本质上使用的是 FieldMapsFieldMap[];当非重复时,使用的还是 FieldMap。

Method parameter reflection 使用反射获取方法参数名

可以使用 java.lang.reflect.Executable.getParameters() 方法获取任何方法或构造方法的形参名称默认情况下,Class 文件不存储形式参数名称,需要使用 javac 编译器的 -parameters 选项编译源文件

Java SE 9

More Concise try-with-resources Statements 更简洁的 try-with-resources 语句

@SafeVarargs Annotation Allowed on Private Instance Methods

@SafeVarargs 注解现在被允许给私有实例方法使用了。

Diamond Syntax and Anonymous Inner Classes

菱形运算符 <> 现在被允许给匿名内部类使用了。

如果您使用下划线字符 _ 作为标识符,您的源代码将无法再被编译。

Support for Private Interface Methods 支持私有接口方法

Java SE 10

Local Variable Type Inference 局部变量类型推断

SyntacticSugar\src\io\weichao\LocalVariableTypeInference.java

1
2
3
4
5
6
7
package io.weichao;

public class LocalVariableTypeInference {
public static void main(String[] args) {
var s = "hello";
}
}

反编译使用的命令:

1
java -jar cfr.jar LocalVariableTypeInference.class

编译后生成:SyntacticSugar\bin\io\weichao\LocalVariableTypeInference.class

反编译后:

解语法糖:

  • 变量类型被还原回原本的类型。

Java SE 11 (LTS 2018.09~2023.09、2026.09)

Java SE 12

Switch Expressions switch 表达式

新型 case 标签具有以下形式:

1
case label_1, label_2, ..., label_n -> expression;|throw-statement;|block 

Java SE 13

Text Blocks 文本块

SyntacticSugar\src\io\weichao\TextBlocks.java

1
2
3
4
5
6
7
8
9
10
package io.weichao;

public class TextBlocks {
public static void main(String[] args) {
String s = """
hello
world
""";
}
}

反编译使用的命令:

1
java -jar cfr.jar TextBlocks.class

编译后生成:SyntacticSugar\bin\io\weichao\TextBlocks.class

反编译后:

解语法糖:

  • 本质是 \n

Switch Expressions switch 表达式

可以在 case 中使用 yield 语句返回需要的值了。

Java SE 14

Pattern Matching for the instanceof Operator instanceof 的模式匹配

你可以指定一个绑定变量;如果 instanceof 运算符的结果是 true,则将被测试的对象分配给绑定变量。

Records 记录

与 enum 一样,记录是类的一种受限形式。它是普通数据载体的理想选择,这些类包含不打算更改的数据并且仅包含构造方法访问器等最基本的方法。

SyntacticSugar\src\io\weichao\Records.java

1
2
3
4
package io.weichao;

record Records(float length, float width) {
}

反编译使用的命令:

1
java -jar cfr.jar Records.class --recordtypes false

编译后生成:SyntacticSugar\bin\io\weichao\Records.class

反编译后:

解语法糖:

  • 本质是一个继承 Record 类的类,修饰符是 final。
  • 方法属性的修饰符是 final,有和方法属性同名的方法用于获取方法属性的值。
  • 有带参的构造方法。
  • 有 toString()、hashCode()、equals() 方法。

Java SE 15

Sealed Classes 密封类

可以控制哪些类可以对定义的类进行扩展。

Java SE 16

Java SE 17 (LTS 2021.09~2026.09、2029.09)

Pattern Matching for switch Expressions and Statements switch 的模式匹配

在早期版本中,选择器表达式的计算结果必须为数字、字符串或 enum 常量,并且 case 标签必须是常量。但是,在此版本中,选择器表达式可以是任何类型,并且 case 标签可以具有模式。

SyntacticSugar\src\io\weichao\SwitchPatternMatching.java

1
2
3
4
5
6
7
8
9
10
11
12
package io.weichao;

public class SwitchPatternMatching {
private void test(Object obj) {
switch (obj) {
case String s -> System.out.println("String: " + s);
case Integer i -> System.out.println("Integer: " + i);
case null -> System.out.println("null");
default -> System.out.println("others");
}
}
}

反编译使用的命令:

1
java -jar cfr.jar SwitchPatternMatching.class

编译后生成:SyntacticSugar\bin\io\weichao\SwitchPatternMatching.class

反编译后:

解语法糖:

  • 调用 SwitchBootstraps.typeSwitch() 方法,计算出数组中匹配的元素的索引null 是 -1

Java SE 18

Pattern Matching for switch Expressions and Statements switch 表达式

  • 常量的 case 标签必须放在受保护模式 case 标签之前。
  • 受保护模式 case 标签必须放在非受保护模式 case 标签之前。

Java SE 19

Pattern Matching for switch Expressions and Statements switch 表达式

  • 受保护模式标签switch 的 case 标签的模式后添加 when 语句
  • 如果选择器表达式的计算结果为 null 并且 switch 没有 null case 标签,则抛出 NullPointerException。
  • 如果 switch 在编译时是详尽的但是在运行时不是,则抛出 MatchException。

Record Patterns 记录的模式

Java SE 20

Java SE 21 (LTS 2023.09~2028.09、2031.09)

参考

  1. Java Object Serialization Specification — 6.4 Grammar for the Stream Format:https://docs.oracle.com/javase/8/docs/platform/serialization/spec/serialTOC.html
  2. Java Lambda 表达式:https://www.runoob.com/java/java8-lambda-expressions.html
  3. Java8——Lambda表达式、方法引用、默认方法的详细介绍与使用案例【一万字】:https://juejin.cn/post/6987569747848232996

Java 语言的语法糖
https://weichao.io/615952530bfd/
作者
魏超
发布于
2022年12月13日
更新于
2023年2月14日
许可协议