惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
Tailwind CSS Blog
C
CERT Recently Published Vulnerability Notes
P
Proofpoint News Feed
Vercel News
Vercel News
博客园 - 三生石上(FineUI控件)
IT之家
IT之家
Help Net Security
Help Net Security
月光博客
月光博客
N
News and Events Feed by Topic
Cloudbric
Cloudbric
博客园 - 司徒正美
L
LangChain Blog
Recent Commits to openclaw:main
Recent Commits to openclaw:main
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
T
Tenable Blog
The Register - Security
The Register - Security
The Hacker News
The Hacker News
I
InfoQ
The Last Watchdog
The Last Watchdog
MyScale Blog
MyScale Blog
Schneier on Security
Schneier on Security
WordPress大学
WordPress大学
小众软件
小众软件
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
宝玉的分享
宝玉的分享
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
K
Kaspersky official blog
L
LINUX DO - 热门话题
N
News | PayPal Newsroom
F
Fortinet All Blogs
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
S
Security @ Cisco Blogs
Recorded Future
Recorded Future
大猫的无限游戏
大猫的无限游戏
H
Help Net Security
Google Online Security Blog
Google Online Security Blog
S
Schneier on Security
C
Cisco Blogs
N
News and Events Feed by Topic
V2EX - 技术
V2EX - 技术
Latest news
Latest news
PCI Perspectives
PCI Perspectives
T
The Blog of Author Tim Ferriss
P
Palo Alto Networks Blog
T
Tor Project blog
Project Zero
Project Zero
云风的 BLOG
云风的 BLOG
Webroot Blog
Webroot Blog
Attack and Defense Labs
Attack and Defense Labs
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org

博客园 - ace--碳水化合物

用ClaudeCode改简历 韦达定理 韦达定理1 梯形1 总平均分 m1 曲线相关 曲线 不等式 抛硬币 错位排列"(全错位)问题 11 1 两圆阴影面积 English1 Tidio 官方帮助中心 ERP 相关系统介绍 蝉翼定理 选配法 利润最大化公式 写在失业的一段话 排列组合-组合 在线简历 概率论与数理统计学习章节 绝对值 比例五大定理 算术平均值与几何平均值 等价符号 公因数与最大公因数 容斥原理 英语2026 机器学习,深度学习,强化学习 分别是什么以及区别是什么 深度学习 监督学习 FAQ IVR TTS选择 k 近邻算法 kaldi 平稳核函数(stationary kernel) Karush-Kuhn-Tucker 条件 凸二次规划(convex quadratic programming) FunASR+FreeSwitch做坐席客服系统记录 freeswitch原理图 客服系统,第三方平台,对话转录音,这些可以不依赖第三方做么 STUN服务器 Gun.js原理 游戏升级记 10 OBB 边界问题 关于yolo26是否可以通过结合java开发 opencv和yolo是一回事情吗 pytorch TorchVision - ace--碳水化合物 PyTorch 和 FashionMNIST 的关系 游戏升级记 9 VibeVoice实现90分钟、多角色播客生成,拓展语音合成新边界 同 WiFi 下用 Claude Code 控制另一台 Windows 电脑 Numpy 1 游戏升级记 8 开源claudecode前端 github star 9k+ 向量数据库skill 游戏升级记 7 游戏升级记 6 关系型数据库,向量数据库,ES,缓存,列式数据库,时序数据库,图数据库等的区别和共同点列举table 多Agent可视化 Agent设计模式 中文 Hermes+Obsidian+LLM wkii,构建AI知识库 Claude Code 前端工程泄露代码开源 游戏升级记 5 ai记忆 Rag 1 游戏升级记 4 游戏升级记 3 游戏升级记 2 AI学习路线 关于hermes agent安装 1 游戏升级记 1 代码迷踪 十三 代码迷踪 十二 ai智能体工程 Rust vs Go 微信小程序实名认证 怎样制作一个街机游戏 跟Claude code说 深入思考 它会思考更深入. googlebusiness profile设置统一白名单的操作,要弹窗模式的 该怎样利用ollama自己训练一个编程大模型 中华AI智能体编程一站式基站构想 打包网站到exe和app 识别的内容一般是试题模板的题号和手写的答题答案(数字) opencv怎么训练 在线SaaS系统做接口版本滚动更新 stripe相关支付流程 流程示意图 error: linker `link.exe` not found | = note: program not found - rust 给我一份关于stripe平台的使用开发说明 c++学习记20260219 奥数-平面几何经典定理 奥数-组合数学 奥数-几何 奥数-代数 奥数-数论
java record
ace--碳水化合物 · 2026-05-14 · via 博客园 - ace--碳水化合物

Java Record 是从 Java 14 开始预览,并在 Java 16 正式发布的特性。它是一种专门用于承载不可变数据的轻量级类,可以看作是一种“具名元组”。它的核心使命是简化数据载体类(Data Carrier)的编写,让你告别为 POJO 重复编写构造器、Getter、equalshashCodetoString 等样板代码的痛苦。

简单来说,以前写一个只有两个字段的类需要几十行代码,用 Record 只需一行。

🔍 核心特性与语法

定义一个 Record 使用 record 关键字:

public record Person(String name, int age) {}

仅此一行,编译器会自动为你生成以下所有内容:

  • 全部字段private final 字段,确保不可变性。
  • 规范构造器:参数顺序与声明顺序一致。
  • 自动 Getter:方法名就是字段名,如 name()age(),没有 get 前缀。
  • equals()hashCode():基于所有组件(字段)值的实现。
  • toString():格式为 Person[name=..., age=...] 的字符串。

它的使用方式和普通类几乎一样,但更具声明性:

Person person = new Person("Alice", 30);
String name = person.name(); // 调用自动生成的Getter
System.out.println(person);  // Person[name=Alice, age=30]

🏗️ 定制与增强

Record 并非完全死板,它允许在紧凑语法上进行有限的定制。

1. 自定义构造器(紧凑构造器)
你可以添加验证逻辑,不需要指定参数列表,也不能给字段赋值(编译器会自动完成):

public record Person(String name, int age) {
    // 这就是紧凑构造器,可以加逻辑
    public Person {
        if (age < 0) {
            throw new IllegalArgumentException("年龄不能为负数");
        }
        // 无需写 this.name = name,编译器会自动生成在末尾
    }
}

2. 添加实例方法或静态方法

public record Person(String name, int age) {
    public String nameToUpperCase() {
        return name.toUpperCase();
    }
    
    public static Person fromAnonymous() {
        return new Person("Anonymous", 0);
    }
}

3. 实现接口
Record 可以自由地实现接口,参与多态。

public interface Greetable { String greet(); }
public record Person(String name, int age) implements Greetable {
    @Override
    public String greet() { return "Hi, I'm " + name; }
}

⚖️ 主要限制

为了实现“纯粹的数据载体”目标,Record 有几条硬性限制,以保证语义的纯粹性:

  1. 不可继承:Record 默认是 final 的,且无法继承其他类。因为它已隐式继承 java.lang.Record 父类。
  2. 所有字段都是 final:保证实例一旦创建就不可变。
  3. 不能有实例字段:除了在记录头中声明的组件(字段),不能再添加其他实例变量。

✅ 适用场景

当你需要创建如下类型的对象时,Record 是完美的选择:

  • DTO (数据传输对象):在应用层间传递数据。
  • VO (值对象):用于领域驱动设计,根据属性来识别身份。
  • API 的返回值:临时组合查询结果。
  • Map 的复合键:当键由多个值决定时,Record 能自动提供正确的 equalshashCode

📝 与其他概念的对比

特性 Java Record 传统 POJO Lombok @Value
语法简洁度 极高,一行声明 极低,需 IDE 生成或手写 高,依赖注解处理
不可变性 语言级保证 需手动实现 注解级保证
本质 一种新的类类型 普通类 编译期代码增强
继承限制 final 类,仅能实现接口 无限制 无限制

💡 代码示例:在 Map 中作为键

import java.util.HashMap;
import java.util.Map;

public class RecordDemo {
    public static void main(String[] args) {
        // 定义坐标记录
        record Coordinate(int x, int y) {}
        
        var map = new HashMap<Coordinate, String>();
        map.put(new Coordinate(1, 2), "宝藏");
        
        // 因为equals/hashCode基于x,y的值,所以能正确取出
        System.out.println(map.get(new Coordinate(1, 2))); // 输出:宝藏
    }
}

总的来说,Java Record 让你在处理纯数据时,代码更简洁、意图更清晰、不可变性更安全。它很适合用来替代那些只需要承载数据的传统类。

Record 本身设计为最终类,如果你需要为基于 Record 的模型提供序列化支持,想了解如何与 Jackson 等 JSON 库搭配使用吗?

当需要将 Java Record 序列化成 JSON 或将 JSON 反序列化为 Record 时,Jackson 提供了很好的支持。下面介绍几种常用方式。

📦 添加依赖

pom.xml 中添加 Jackson 依赖(以 2.15 为例):

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.15.2</version>
</dependency>

✅ 基础使用:开箱即用

从 Jackson 2.12 开始,对 Record 的支持是内置且无需额外注解的,可以直接序列化和反序列化。

1. Record 定义

public record Person(String name, int age, String email) {}

2. 序列化与反序列化

import com.fasterxml.jackson.databind.ObjectMapper;

public class RecordJsonDemo {
    public static void main(String[] args) throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        
        // Java Record -> JSON 字符串
        Person person = new Person("张三", 28, "zhangsan@example.com");
        String json = mapper.writeValueAsString(person);
        System.out.println("JSON: " + json);
        // 输出: JSON: {"name":"张三","age":28,"email":"zhangsan@example.com"}
        
        // JSON 字符串 -> Java Record
        String jsonStr = "{\"name\":\"李四\",\"age\":35,\"email\":\"lisi@example.com\"}";
        Person deserializedPerson = mapper.readValue(jsonStr, Person.class);
        System.out.println("反序列化: " + deserializedPerson);
        // 输出: 反序列化: Person[name=李四, age=35, email=lisi@example.com]
    }
}

🎨 定制字段名称:@JsonProperty

若 JSON 字段名与 Record 组件名不一致,使用 @JsonProperty

public record Person(
    @JsonProperty("full_name") String name,
    @JsonProperty("years_old") int age,
    String email
) {}

🏗️ 自定义构造器反序列化

对于需要验证或转换的场景,可以在紧凑构造器中处理:

import com.fasterxml.jackson.annotation.JsonCreator;

public record Person(String name, int age, String email) {
    @JsonCreator  // 明确指示 Jackson 使用此构造器
    public Person {
        if (age < 0) throw new IllegalArgumentException("年龄非法");
        // 可以做一些规范化,比如 trim
    }
}

🧩 空值处理与可选字段

使用 Optional 需特别注意:Jackson 默认能处理 Optional 类型,但更推荐用 @JsonInclude 控制非空序列化,或在反序列化时给默认值。

为字段设置默认值(反序列化时若 JSON 缺失该字段):

public record Person(String name, int age, String email) {
    // 手工重载规范构造器,在内部提供默认值
    @JsonCreator
    public Person(@JsonProperty("name") String name, 
                  @JsonProperty("age") int age, 
                  @JsonProperty("email") String email) {
        this.name = name == null ? "未知" : name;
        this.age = age;
        this.email = email == null ? "no-email@example.com" : email;
    }
}

⚠️ 常见注意事项

  1. 字段顺序:Jackson 按 Record 组件声明顺序进行序列化/反序列化,务必保持一致。
  2. 空构造函数:Record 没有无参构造器,反序列化要求 JSON 必须提供所有字段。如果某些字段可能缺失,请像上面那样提供带默认值的构造器。
  3. 内部类 Record:若 Record 定义在另一个类内部,确保是静态的(static record),否则 Jackson 可能无法实例化。

💡 实际应用示例

假设一个 API 返回用户信息,Record 作为 DTO:

// 定义 Record
public record ApiResponse<T>(int code, String message, T data) {}

// 在 Controller 中使用 (Spring Boot 示例)
@GetMapping("/user/{id}")
public ApiResponse<Person> getUser(@PathVariable int id) {
    Person user = new Person("王五", 30, "wangwu@example.com");
    return new ApiResponse<>(200, "成功", user);
}
// 返回的 JSON 自动为: {"code":200,"message":"成功","data":{"name":"王五","age":30,"email":"wangwu@example.com"}}

Jackson 从 2.12 起对 Record 的自动化支持已经相当成熟,无需额外插件或复杂配置即可无缝集成到 Spring Boot 等框架中使用。