文章目录
- [85. Java Record 深入解析:构造函数、访问器、序列化与实际应用](#85. Java Record 深入解析:构造函数、访问器、序列化与实际应用)
- 
- **第一部分:构造函数(Constructor)**
- 
- [**1. 记录的构造函数定义**](#1. 记录的构造函数定义)
 
- **第二部分:访问器(Accessor)**
- 
- [**2.1 记录的默认访问器**](#2.1 记录的默认访问器)
- [**2.2 自定义访问器**](#2.2 自定义访问器)
 
- **第三部分:记录的序列化(Serialization)**
- [**第四部分:实际应用案例(Use Case)**](#第四部分:实际应用案例(Use Case))
- 
- [**4.1 计算拥有最多城市的州**](#4.1 计算拥有最多城市的州)
- 
- **第一步:构建(Histogram)**
- [1. 初始化空列表](#1. 初始化空列表)
- [2. 流操作与分组统计](#2. 流操作与分组统计)
- **第二步:找到最多城市的州**
- [**第三步:使用 `Record` 提升可读性**](#第三步:使用 Record提升可读性)
 
 
 
85. Java Record 深入解析:构造函数、访问器、序列化与实际应用
第一部分:构造函数(Constructor)
1. 记录的构造函数定义
大家好,今天我们来深入学习 Java 的 Record(记录)类型。🚀
在 Java 中,Record是一种特殊的类 ,主要用于存储数据。每个Record都会自动生成构造函数、toString()、equals()、hashCode() 方法,使其非常适合用作数据载体。
我们可以为一个 Record 自定义构造函数,只要这个构造函数调用了该Record的主构造函数(canonical constructor)。
来看看下面的例子:
            
            
              java
              
              
            
          
          public record State(String name, String capitalCity, List<String> cities) {
    
    // 紧凑构造函数(Compact Constructor)
    public State {
        cities = List.copyOf(cities); // 创建不可变列表,防止外部修改
    }
    
    // 无城市列表的构造函数
    public State(String name, String capitalCity) {
        this(name, capitalCity, List.of());
    }
    
    // 变长参数的构造函数
    public State(String name, String capitalCity, String... cities) {
        this(name, capitalCity, List.of(cities));
    }
}🔹 这里的关键点:
- 紧凑构造函数 (Compact Constructor)不需要显式列出参数,它的作用是在构造过程中做一些额外处理,比如这里的 List.copyOf(cities),确保cities不可变。
- 重载构造函数 :我们定义了两个额外的构造函数,一个用于不包含 cities的情况,另一个允许使用 可变参数(varargs) 传递城市名称。
- this()调用 :在 Java 中,构造函数的第一行 必须调用同一个类的另一个构造函数或- super()。
💡 示例应用场景 : 假设你的程序中有一个 State 对象,它存储了某个州的名字、首府,以及一些城市信息。你希望:
- 确保城市列表不会在外部被修改。
- 允许用户创建不含城市列表 的 State对象。
- 允许用户直接传入多个城市名称,而不是自己创建 List<String>。
这时,多个构造函数的设计就能满足不同的需求! ✅
第二部分:访问器(Accessor)
2.1 记录的默认访问器
💡 大家还记得吗? 记录类型会自动生成访问器 (accessor),即字段名相同的方法。
比如:
            
            
              java
              
              
            
          
          public record Point(int x, int y) {}这个 Point 记录会自动生成两个方法:
            
            
              java
              
              
            
          
          public int x() { return x; }
public int y() { return y; }这样,我们可以直接调用 point.x() 和 point.y() 获取值。
2.2 自定义访问器
有时候,默认的访问器不够用,比如 State 记录中,如果没有在构造函数中做防御性拷贝,我们可以在访问器中返回一个不可变副本:
            
            
              java
              
              
            
          
          public List<String> cities() {
    return List.copyOf(cities);
}这样,每次访问 cities() 时都会返回一个不可变的列表,避免外部修改原始数据。
第三部分:记录的序列化(Serialization)
如果需要让 Record 支持序列化 (Serialization),只需实现 Serializable 接口:
            
            
              java
              
              
            
          
          public record State(String name, String capitalCity, List<String> cities) implements Serializable {}但是,要注意:
- 不能使用 writeObject()和readObject()方法自定义序列化行为。
- 不能实现 Externalizable。
- 反序列化时,始终会调用 主构造函数(Canonical Constructor),确保所有的校验逻辑都会被执行。
💡 示例场景 : 如果你的应用程序需要在不同服务之间传输 State 对象,使用 Record 可以确保数据一致性,防止意外修改。
第四部分:实际应用案例(Use Case)
4.1 计算拥有最多城市的州
假设我们有一个 City 记录和 Zhou 记录:
            
            
              java
              
              
            
          
          public record City(String name, Zhou state) {}
public record Zhou(String name) {}我们有一个 List<City>,需要找到拥有最多城市的州。
第一步:构建(Histogram)
            
            
              java
              
              
            
          
          List<City> cities = List.of(
    new City("New York", new Zhou("NY")),
    new City("Los Angeles", new Zhou("CA")),
    new City("San Francisco", new Zhou("CA"))
);
Map<Zhou, Long> numberOfCitiesPerState =
    cities.stream()
          .collect(Collectors.groupingBy(
                   City::state, Collectors.counting()
          ));
 System.out.println(numberOfCitiesPerState);这里,我们用 Collectors.groupingBy() 来计算每个州的城市数量。
1. 初始化空列表
            
            
              java
              
              
            
          
          List<City> cities = List.of();- 作用 :创建一个不可变的空列表,类型为 City。
- 特点:
- Java 9+ 支持的工厂方法,语法简洁。
- 列表一旦创建,无法添加或删除元素(不可变)。
 
2. 流操作与分组统计
            
            
              java
              
              
            
          
          Map<Zhou, Long> numberOfCitiesPerState =
    cities.stream()
          .collect(Collectors.groupingBy(
                   City::state, Collectors.counting()
          ));- 流程分解 :
- cities.stream():将列表转换为流(Stream),以便进行链式操作。
- collect(Collectors.groupingBy(...)):使用收集器对流元素进行分组统计。- City::state:分组依据为- City对象的- state属性(即州)。
- Collectors.counting():统计每个分组的元素数量。
 
 
- 结果 :
- 返回一个 Map<Zhou, Long>,键是州对象,值是该州的城市数量。
- 示例 :若有 3 个城市属于 "California" 州,则 Map中会有键值对California → 3L。
 
- 返回一个 
第二步:找到最多城市的州
            
            
              java
              
              
            
          
          Map.Entry<State, Long> stateWithTheMostCities =
    numberOfCitiesPerState.entrySet().stream()
                          .max(Map.Entry.comparingByValue())
                          .orElseThrow();但这样写可读性不高 ,因为 Map.Entry<State, Long> 只是个键值对,缺乏语义信息。
第三步:使用 Record 提升可读性
我们可以定义一个新的 Record,表示 "某个州及其城市数量":
            
            
              java
              
              
            
          
          record NumberOfCitiesPerState(Zhou state, long numberOfCities) {
    public NumberOfCitiesPerState(Map.Entry<State, Long> entry) {
        this(entry.getKey(), entry.getValue());
    }
    
    public static Comparator<NumberOfCitiesPerState> comparingByNumberOfCities() {
        return Comparator.comparing(NumberOfCitiesPerState::numberOfCities);
    }
}然后,优化 max() 代码,使其更具可读性:
            
            
              java
              
              
            
          
          NumberOfCitiesPerState stateWithTheMostCities =
    numberOfCitiesPerState.entrySet().stream()
                          .map(NumberOfCitiesPerState::new)
                          .max(NumberOfCitiesPerState.comparingByNumberOfCities())
                          .orElseThrow();✅ 优势:
- 代码更加清晰,NumberOfCitiesPerState让业务逻辑一目了然。
- 避免直接操作 Map.Entry,提高可读性和可维护性。
✨ 总结
- 记录类(Record) 适用于不可变数据模型。
- 自定义构造函数 可增强安全性,如防御性拷贝。
- 访问器方法 可定制逻辑,增强封装。
- 序列化时,主构造函数始终被调用,保证一致性。
- 使用 Record 提高代码可读性和业务逻辑清晰度。