背景
JEP 395: Records 中提到 <math xmlns="http://www.w3.org/1998/Math/MathML"> JDK 16 \text{JDK 16} </math>JDK 16 正式支持记录类( <math xmlns="http://www.w3.org/1998/Math/MathML"> Record Classes \text{Record Classes} </math>Record Classes)。那么记录类在 <math xmlns="http://www.w3.org/1998/Math/MathML"> class \text{class} </math>class 文件中长什么样子呢?让我们一起来探索吧。
要点
- 规范构造函数( <math xmlns="http://www.w3.org/1998/Math/MathML"> canonical constructor \text{canonical constructor} </math>canonical constructor)
- 如果我们在记录类中没有定义构造函数,那么记录类中会出现隐式的 <math xmlns="http://www.w3.org/1998/Math/MathML"> canonical constructor \text{canonical constructor} </math>canonical constructor
- 对每个 <math xmlns="http://www.w3.org/1998/Math/MathML"> Record Component \text{Record Component} </math>Record Component 而言
- 记录类中会有一个与之对应的 <math xmlns="http://www.w3.org/1998/Math/MathML"> private \text{private} </math>private, <math xmlns="http://www.w3.org/1998/Math/MathML"> final \text{final} </math>final, 非 <math xmlns="http://www.w3.org/1998/Math/MathML"> static \text{static} </math>static 字段
- 记录类中会有一个与之对应的被称为 <math xmlns="http://www.w3.org/1998/Math/MathML"> accessor method \text{accessor method} </math>accessor method 的方法
正文
例子
我从 JEP 395: Records 里找了个例子 ⬇️ (略有改动)
java
public record Point(int x, int y) {
}
我们将上述代码保存为 <math xmlns="http://www.w3.org/1998/Math/MathML"> Point.java \text{Point.java} </math>Point.java。下方的命令可以编译 <math xmlns="http://www.w3.org/1998/Math/MathML"> Point.java \text{Point.java} </math>Point.java (请注意,所使用的 <math xmlns="http://www.w3.org/1998/Math/MathML"> javac \text{javac} </math>javac 版本应当高于或者等于 <math xmlns="http://www.w3.org/1998/Math/MathML"> 16 \text{16} </math>16)
bash
javac Point.java
编译后,会得到 <math xmlns="http://www.w3.org/1998/Math/MathML"> Point.class \text{Point.class} </math>Point.class 文件。使用如下命令可以查看 <math xmlns="http://www.w3.org/1998/Math/MathML"> Point.class \text{Point.class} </math>Point.class 的内容
bash
javap -p Point
这个命令在我电脑上运行的结果如下 ⬇️
text
Compiled from "Point.java"
public final class Point extends java.lang.Record {
private final int x;
private final int y;
public Point(int, int);
public final java.lang.String toString();
public final int hashCode();
public final boolean equals(java.lang.Object);
public int x();
public int y();
}
我画了对应的类图 ⬇️

在反编译的结果中,我们看到了
- <math xmlns="http://www.w3.org/1998/Math/MathML"> Point \text{Point} </math>Point 的构造函数
- <math xmlns="http://www.w3.org/1998/Math/MathML"> x x </math>x 字段和 <math xmlns="http://www.w3.org/1998/Math/MathML"> y y </math>y 字段
- <math xmlns="http://www.w3.org/1998/Math/MathML"> x ( ) x() </math>x() 方法和 <math xmlns="http://www.w3.org/1998/Math/MathML"> y ( ) y() </math>y() 方法
但是我们在 <math xmlns="http://www.w3.org/1998/Math/MathML"> Point.java \text{Point.java} </math>Point.java 中 并没有 显式定义这些内容,那么它们是从哪里来的呢?
规范构造函数(Canonical Constructor)
如果我们在记录类没有显式定义构造函数,那么记录类中会出现隐式的 <math xmlns="http://www.w3.org/1998/Math/MathML"> canonical constructor \text{canonical constructor} </math>canonical constructor。 8.10.4. Record Constructor Declarations 提到了相关细节 ⬇️

可以用如下命令查看 <math xmlns="http://www.w3.org/1998/Math/MathML"> Point.class \text{Point.class} </math>Point.class 的详细内容
bash
javap -v -p Point
完整的结果比较长,其中和构造函数相关的部分如下
text
public Point(int, int);
descriptor: (II)V
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=3, args_size=3
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
LineNumberTable:
line 1: 0
MethodParameters:
Name Flags
x
y
我们可以手动对其进行反编译 ⬇️
java
// 以下内容是我手动反编译的结果,仅供参考
public Record(int x, int y) {
super();
this.x = x;
this.y = y;
}
Record Components
在 8.10. Record Classes 中提到了 <math xmlns="http://www.w3.org/1998/Math/MathML"> Record Components \text{Record Components} </math>Record Components ⬇️ (重要的部分我用绿色框和绿色箭头标了出来)

对 <math xmlns="http://www.w3.org/1998/Math/MathML"> Point \text{Point} </math>Point 这个记录类而言,以下两者都是它的 <math xmlns="http://www.w3.org/1998/Math/MathML"> Record Component \text{Record Component} </math>Record Component
- <math xmlns="http://www.w3.org/1998/Math/MathML"> int x \text{int x} </math>int x
- <math xmlns="http://www.w3.org/1998/Math/MathML"> int y \text{int y} </math>int y
8.10.3. Record Members 提到了如下内容 ⬇️ (重要的部分我用绿色框标了出来)

对每个 <math xmlns="http://www.w3.org/1998/Math/MathML"> Record Component \text{Record Component} </math>Record Component 而言,
- 记录类中会有一个与之对应的 <math xmlns="http://www.w3.org/1998/Math/MathML"> private \text{private} </math>private, <math xmlns="http://www.w3.org/1998/Math/MathML"> final \text{final} </math>final, 非 <math xmlns="http://www.w3.org/1998/Math/MathML"> static \text{static} </math>static 字段
- 记录类中会有一个与之对应的被称为 <math xmlns="http://www.w3.org/1998/Math/MathML"> accessor method \text{accessor method} </math>accessor method 的方法
我们可以用如下的命令查看 <math xmlns="http://www.w3.org/1998/Math/MathML"> Point.class \text{Point.class} </math>Point.class 的详细内容
bash
javap -v -p Point
完整的结果比较长,其中和 <math xmlns="http://www.w3.org/1998/Math/MathML"> Record Components \text{Record Components} </math>Record Components 相关的部分如下
text
private final int x;
descriptor: I
flags: (0x0012) ACC_PRIVATE, ACC_FINAL
private final int y;
descriptor: I
flags: (0x0012) ACC_PRIVATE, ACC_FINAL
public int x();
descriptor: ()I
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: getfield #7 // Field x:I
4: ireturn
LineNumberTable:
line 1: 0
public int y();
descriptor: ()I
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: getfield #13 // Field y:I
4: ireturn
LineNumberTable:
line 1: 0
我们可以手动对其进行反编译 ⬇️
java
// 以下内容是我手动反编译的结果,仅供参考
private final int x;
private final int y;
public int x() {
return this.x;
}
public int y() {
return this.y;
}
参考资料
其他
画 Point 的类图所用到的代码
puml
@startuml
'https://plantuml.com/class-diagram
@startuml
title <i>Point</i> 的类图
caption \n\n
' caption 的内容只是为了防止掘金平台生成的水印遮盖图中的文字
abstract java.lang.Record
class Point
java.lang.Record <-- Point
abstract java.lang.Record {
# Record()
+ {abstract} boolean equals(Object)
+ {abstract} int hashCode()
+ {abstract} String toString()
}
class Point {
- final int x
- final int y
+ Point(int, int)
+ final String toString()
+ final int hashCode()
+ final boolean equals(Object)
+ int x()
+ int y()
}
note left of java.lang.Record::equals
override 了 <i>java.lang.Object</i> 中的方法
end note
note left of java.lang.Record::hashCode
override 了 <i>java.lang.Object</i> 中的方法
end note
note left of java.lang.Record::toString
override 了 <i>java.lang.Object</i> 中的方法
end note
@enduml