认识记录类
将记录类声明为简洁的不可变数据载体,并理解其自动生成的成员
认识记录类 是 CoddyKit 上的免费 Java Academy 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Java Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Java Academy 课程共包含 4 节课。
认识记录类
Java 记录类(Java 16+)是一种简洁的不可变数据载体类声明方式。它会自动生成样板代码,包括构造函数、访问器、equals、hashCode 和 toString。
声明记录类
使用 record 关键字,后面跟记录类名称和组件列表。每个组件都会成为一个私有的不可重新赋值字段,并生成一个公共访问器方法。
record Point(double x, double y) {}
Point p = new Point(3.0, 4.0);
System.out.println(p.x()); // 3.0 (accessor, not getX())
System.out.println(p.y()); // 4.0
System.out.println(p); // Point[x=3.0, y=4.0]
System.out.println(p.equals(new Point(3.0, 4.0))); // true自动生成的成员
编译器会为每个记录类自动生成:规范构造函数、公共访问器、equals()、hashCode() 和 toString()。您无需手动编写这些内容。
record Product(String name, double price, int stock) {}
Product p1 = new Product("Widget", 9.99, 100);
Product p2 = new Product("Widget", 9.99, 100);
System.out.println(p1.name()); // Widget
System.out.println(p1.price()); // 9.99
System.out.println(p1.equals(p2)); // true (structural)
System.out.println(p1.hashCode() == p2.hashCode()); // true
System.out.println(p1); // Product[name=Widget, price=9.99, stock=100]记录类是不可变的
记录类的组件是 final 的。构造完成后,您无法修改它们——记录类用于表示不可变值对象。
record Money(long cents, String currency) {}
Money price = new Money(999, "USD");
// price.cents = 500; // Compile error — final field!
// To "update" a record, create a new one
Money discounted = new Money(price.cents() - 100, price.currency());
System.out.println(discounted); // Money[cents=899, currency=USD]记录类只能继承 Object
记录类会隐式继承 java.lang.Record,不能继承其他类,但可以实现接口。
interface Describable { String describe(); }
record Circle(double radius) implements Describable {
public String describe() {
return String.format("Circle with radius %.1f", radius);
}
public double area() {
return Math.PI * radius * radius;
}
}
Circle c = new Circle(5.0);
System.out.println(c.describe()); // Circle with radius 5.0
System.out.printf("Area: %.2f%n", c.area()); // Area: 78.54集合中的记录类
由于记录类会根据所有组件正确实现 equals 和 hashCode,因此可以正确地用作映射键和集合元素。
import java.util.*;
record Coordinate(int row, int col) {}
Map<Coordinate, String> grid = new HashMap<>();
grid.put(new Coordinate(0, 0), "Origin");
grid.put(new Coordinate(1, 2), "Target");
// Lookup with equal-valued record works correctly
System.out.println(grid.get(new Coordinate(0, 0))); // Origin
System.out.println(grid.get(new Coordinate(1, 2))); // Target
Set<Coordinate> visited = new HashSet<>();
visited.add(new Coordinate(3, 3));
System.out.println(visited.contains(new Coordinate(3, 3))); // true作为 DTO 的记录类
记录类非常适合用于 REST API 中的数据传输对象(DTO)——它们可以在各层之间传递数据,而不包含行为。
record CreateUserRequest(String email, String displayName, String plan) {}
record UserResponse(long id, String email, String displayName,
String plan, String createdAt) {}
// Simulating a service call
static UserResponse createUser(CreateUserRequest req) {
long id = 42L;
return new UserResponse(id, req.email(), req.displayName(),
req.plan(), java.time.Instant.now().toString());
}
CreateUserRequest req = new CreateUserRequest("alice@example.com", "Alice", "PRO");
UserResponse resp = createUser(req);
System.out.println(resp);解构记录类
记录类非常适合与模式匹配结合使用。您可以在 switch 表达式中解构记录类(Java 21+)。
sealed interface Shape permits Circle, Rectangle, Triangle {}
record Circle(double radius) implements Shape {}
record Rectangle(double width, double height) implements Shape {}
record Triangle(double base, double height) implements Shape {}
static double area(Shape s) {
return switch (s) {
case Circle(double r) -> Math.PI * r * r;
case Rectangle(double w, double h) -> w * h;
case Triangle(double b, double h) -> 0.5 * b * h;
};
}
System.out.printf("%.2f%n", area(new Circle(5))); // 78.54
System.out.printf("%.2f%n", area(new Rectangle(4, 6))); // 24.00记录类的序列化
记录类声明为实现 Serializable 后,会自然地支持序列化。它们开箱即用地适用于 JSON 库(Jackson、Gson)。
// With Jackson (add @JsonProperty if needed)
import com.fasterxml.jackson.databind.ObjectMapper;
record OrderSummary(String orderId, double total, String status) {}
ObjectMapper mapper = new ObjectMapper();
OrderSummary order = new OrderSummary("ORD-001", 149.99, "SHIPPED");
String json = mapper.writeValueAsString(order);
System.out.println(json);
// {"orderId":"ORD-001","total":149.99,"status":"SHIPPED"}
OrderSummary restored = mapper.readValue(json, OrderSummary.class);
System.out.println(order.equals(restored)); // true嵌套记录类
记录类可以将其他记录类作为组件,从而构建丰富的不可变对象图。
record Address(String street, String city, String country) {}
record Customer(long id, String name, Address address) {}
Address addr = new Address("123 Main St", "New York", "US");
Customer customer = new Customer(1L, "Alice", addr);
System.out.println(customer.name()); // Alice
System.out.println(customer.address().city()); // New York
System.out.println(customer); // Customer[id=1, name=Alice, address=Address[street=123 Main St, city=New York, country=US]]记录类的限制
记录类有一些需要注意的限制:
- 不能继承其他类(会隐式继承 Record)
- 组件始终不可重新赋值——不能包含可变状态
- 不能在组件列表之外声明实例字段
- 不能声明为抽象类
// Records CAN have static fields and methods
record Temperature(double celsius) {
static final double ABSOLUTE_ZERO = -273.15;
public double toFahrenheit() {
return celsius * 9.0/5.0 + 32;
}
public static Temperature fromFahrenheit(double f) {
return new Temperature((f - 32) * 5.0/9.0);
}
}
System.out.println(Temperature.fromFahrenheit(98.6)); // Temperature[celsius=37.0]快速检查
Java 编译器会为记录类自动生成哪些成员?
回顾:认识记录类
要点:
- 记录类只需极少的样板代码即可声明不可变数据载体
- 自动生成:规范构造函数、访问器、equals、hashCode 和 toString
- 组件不可重新赋值——记录类在设计上是不可变的
- 记录类可以实现接口,但不能继承类
- 非常适合数据传输对象、值对象和 API 响应
- 由于采用结构相等性,可以正确地用作 HashMap 的键和 HashSet 的元素
常见问题解答
「认识记录类」课时是免费的吗?
是的 — 「认识记录类」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Java Academy 课程的其余内容,请升级到 CoddyKit PRO。 Java Academy 课程共包含 4 节课。
「认识记录类」这节课中我会学到什么?
将记录类声明为简洁的不可变数据载体,并理解其自动生成的成员 你通过在浏览器中直接运行的动手代码来练习 Java Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Java Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Java Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。
「认识记录类」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Java Academy 课中编写并运行代码吗?
能。每节 Java Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。