1. 使用 Map 实现临时数据容器(通用性强)
import java.util.HashMap;
import java.util.Map;
public class TempDataExample {
public static void main(String[] args) {
// 使用 Map 存储临时数据
Map<String, Object> tempData = new HashMap<>();
// 添加数据(无需定义类)
tempData.put("name", "张三");
tempData.put("age", 25);
tempData.put("isStudent", true);
// 访问数据(需类型转换)
String name = (String) tempData.get("name");
int age = (int) tempData.get("age");
boolean isStudent = (boolean) tempData.get("isStudent");
System.out.println("姓名: " + name); // 输出:姓名: 张三
System.out.println("年龄: " + age); // 输出:年龄: 25
System.out.println("是否学生: " + isStudent); // 输出:是否学生: true
}
}
优点:
无需定义任何类,直接使用键值对存储数据。
灵活支持不同类型的数据(Object 类型)。
缺点:
类型不安全:需要手动强制类型转换,可能导致运行时错误。
键名硬编码:容易因拼写错误导致数据访问失败。
2. 使用 record 实现临时数据容器(Java 16+,类型安全)
public class RecordExample {
// 定义一个不可变的临时数据容器(record)
public record TempData(String name, int age, boolean isStudent) {}
public static void main(String[] args) {
// 创建临时数据实例
TempData data = new TempData("李四", 30, false);
// 直接访问字段(无需Getter)
System.out.println("姓名: " + data.name()); // 输出:姓名: 李四
System.out.println("年龄: " + data.age()); // 输出:年龄: 30
System.out.println("是否学生: " + data.isStudent()); // 输出:是否学生: false
}
}
优点:
类型安全:字段类型明确,无需强制转换。
代码简洁:自动生成equals()、hashCode()和toString()方法。
不可变性:数据创建后不可修改,避免副作用。
缺点:
需要提前定义record类(适合需要复用或明确结构的场景)。
3. 匿名内部类实现(不推荐,仅演示)
import java.lang.reflect.Field;
public class AnonymousDataContainerDemo {
public static void main(String[] args) {
// 1. 创建匿名内部类实例作为临时数据容器
Object dataContainer = new Object() {
// 定义临时字段
String carNumber = "京A12345";
int speed = 120;
boolean isElectric = true;
};
// 2. 通过反射访问字段(需处理异常)
try {
// 获取匿名类的Class对象
Class<?> clazz = dataContainer.getClass();
// 访问字段 carNumber
Field carNumberField = clazz.getDeclaredField("carNumber");
String carNumber = (String) carNumberField.get(dataContainer);
// 访问字段 speed
Field speedField = clazz.getDeclaredField("speed");
int speed = (int) speedField.get(dataContainer);
// 访问字段 isElectric
Field isElectricField = clazz.getDeclaredField("isElectric");
boolean isElectric = (boolean) isElectricField.get(dataContainer);
// 输出结果
System.out.println("车牌号: " + carNumber); // 京A12345
System.out.println("车速: " + speed + "km/h"); // 120km/h
System.out.println("是否电车: " + isElectric); // true
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
}
}
关键特性与注意事项
1. 匿名内部类特性
语法结构:new Object() { … } 定义了一个继承自Object的匿名子类
字段定义:可以自由添加字段(如carNumber、speed),但外部无法直接访问
2. 反射访问机制
必要性:因引用类型为Object,必须通过反射访问子类字段
风险点:
字段名硬编码(如"carNumber"),拼写错误会导致NoSuchFieldException
强制类型转换错误会引发ClassCastException
3. 性能与安全
性能损耗:反射操作比直接访问慢约 10-100 倍(实测数据)
类型安全:编译期无法检查类型,所有错误均在运行时暴露
适用场景(谨慎使用)
快速原型验证:临时测试数据存储
封闭环境:确定代码不会被复用时
必须重写方法:例如需要自定义toString():
Object debugData = new Object() {
int errorCode = 404;
String message = "Not Found";
@Override
public String toString() {
return "Error[" + errorCode + "]: " + message;
}
};
System.out.println(debugData); // 输出:Error[404]: Not Found
结论
虽然匿名内部类可以实现临时数据存储,但强烈推荐使用以下方案:
简单数据 → Map<String, Object>
结构化数据 → record 或显式定义类
需要方法重写 → 合理使用匿名内部类(仅限方法覆盖,非数据存储)
匿名内部类作为数据容器的实际工程价值极低,建议仅用于学习内部类机制的特殊场景。
主要介绍一下不可变数据载体的具体实现
1. record 基础语法
record 是 Java 16+ 引入的特性,用于快速定义不可变数据载体类。其核心特点:
自动生成:字段、构造方法、equals()、hashCode()、toString()
不可变性:所有字段默认 final
类型安全:字段类型明确,无反射或类型转换风险
// 定义一个名为 CarData 的 record
public record CarData(String carNumber, int speed, boolean isElectric) {}
2. 完整示例:用 record 替代匿名内部类
public class RecordExample {
// 定义 record(相当于一个不可变的数据容器)
public record CarData(
String carNumber, // 车牌号
int speed, // 车速
boolean isElectric // 是否电车
) {}
public static void main(String[] args) {
// 创建实例(不可变)
CarData data = new CarData("京A12345", 120, true);
// 直接访问字段(无需Getter)
System.out.println("车牌号: " + data.carNumber()); // 京A12345
System.out.println("车速: " + data.speed() + "km/h"); // 120km/h
System.out.println("是否电车: " + data.isElectric()); // true
// 自动生成的 toString()
System.out.println(data);
// 输出: CarData[carNumber=京A12345, speed=120, isElectric=true]
}
}
3. 对比匿名内部类的优势
4. record 的高级用法
(1) 自定义验证逻辑
public record CarData(String carNumber, int speed, boolean isElectric) {
// 自定义构造方法,添加验证逻辑
public CarData {
if (speed < 0) throw new IllegalArgumentException("车速不能为负");
if (carNumber == null) throw new IllegalArgumentException("车牌号必填");
}
}
(2) 添加工具方法
public record CarData(String carNumber, int speed, boolean isElectric) {
// 添加自定义方法
public String toDisplayString() {
return String.format("%s (%d km/h)%s",
carNumber, speed, isElectric ? " [电车]" : "");
}
}
// 使用
CarData data = new CarData("沪B98765", 80, true);
System.out.println(data.toDisplayString()); // 沪B98765 (80 km/h) [电车]
5. 适用场景
DTO 对象:API 接口参数/返回值的结构化数据
配置参数:需要明确字段的业务配置
临时数据容器:替代 Map 或匿名内部类的临时存储需求
不可变数据:如缓存数据、并发共享数据
6. 低版本 Java 的替代方案
如果项目使用 Java 8-15,可以通过以下方式模拟类似效果:
(1) 手动编写不可变类
public final class CarData {
private final String carNumber;
private final int speed;
private final boolean isElectric;
public CarData(String carNumber, int speed, boolean isElectric) {
this.carNumber = carNumber;
this.speed = speed;
this.isElectric = isElectric;
}
// 手动添加 Getter、equals、hashCode、toString...
}
(2) 使用 Lombok 简化
@Value // 生成不可变类(等效于 final + 所有字段 private final)
public class CarData {
String carNumber;
int speed;
boolean isElectric;
}
record的具体实现
1. 在 record 中添加方法的三种方式
record 允许定义以下内容:
静态方法:用于工具操作
实例方法:添加业务逻辑
自定义构造方法:实现参数验证
2. 完整示例:带实现的 record 类
public record User(
String username,
String email,
int age
) {
// ---------- 1. 自定义构造函数(添加验证逻辑)----------
public User {
// 参数校验
if (username == null || username.isBlank()) {
throw new IllegalArgumentException("用户名不能为空");
}
if (age < 0) {
throw new IllegalArgumentException("年龄不能为负");
}
// 自动赋值(编译器隐式完成)
}
// ---------- 2. 添加实例方法 ----------
public String getDisplayInfo() {
return String.format("%s (%d 岁)", username, age);
}
public boolean isAdult() {
return age >= 18;
}
// ---------- 3. 添加静态方法 ----------
public static User createDefaultUser() {
return new User("guest", "guest@example.com", 18);
}
// ---------- 4. 实现接口 ----------
public interface Serializable {
String toJson();
}
// 实现接口方法
public String toJson() {
return String.format(
"{\"username\":\"%s\",\"email\":\"%s\",\"age\":%d}",
username, email, age
);
}
}
3. 使用示例
public class Main {
public static void main(String[] args) {
// 创建实例(自动调用自定义构造函数)
User user = new User("张三", "zhangsan@example.com", 25);
// 调用实例方法
System.out.println(user.getDisplayInfo()); // 张三 (25 岁)
System.out.println("是否成人: " + user.isAdult()); // true
// 调用静态方法
User defaultUser = User.createDefaultUser();
System.out.println(defaultUser); // User[username=guest, email=guest@example.com, age=18]
// 调用接口方法
System.out.println(user.toJson());
// {"username":"张三","email":"zhangsan@example.com","age":25}
}
}
4. 关键规则与最佳实践
(1) 不可变性保障
所有字段默认 final,外部无法修改
禁止添加 setter 方法(与设计初衷冲突)
(2) 方法设计原则
无副作用:方法应仅基于现有字段计算新值,不修改状态
纯函数优先:推荐定义类似 withAge(int newAge) 的方法返回新对象:
(1) 不可变性保障
所有字段默认 final,外部无法修改
禁止添加 setter 方法(与设计初衷冲突)
(2) 方法设计原则
无副作用:方法应仅基于现有字段计算新值,不修改状态
纯函数优先:推荐定义类似 withAge(int newAge) 的方法返回新对象:
(3) 接口实现
可自由实现多个接口
示例:添加序列化能力
public record Product(String name, double price) implements Serializable, Comparable<Product> {
@Override
public int compareTo(Product other) {
return Double.compare(this.price, other.price);
}
}
(4) 继承限制
record 不能显式继承其他类(隐含继承 Record)
但可以实现接口
5. 与传统类的对比
6. 适用场景
1、DTO 对象:API 接口参数/返回值
public record ApiResponse<T>(int code, String message, T data) {}
2、配置参数:明确字段的业务配置
public record DatabaseConfig(String url, String user, String password) {}
3、计算结果:如坐标点、颜色值等
public record Point(double x, double y) {
public double distanceTo(Point other) {
return Math.hypot(x - other.x, y - other.y);
}
}
总结
Java record 的本质:是语法糖,编译后生成标准的不可变类
实现扩展方式:通过添加方法、自定义构造、实现接口
核心优势:在保证不可变性的同时,允许添加轻量级业务逻辑
适用边界:适合数据载体场景,复杂业务逻辑仍需使用传统类
record不可变的表现
1. record 的不可变性特性
record 的不可变性由以下机制保证:
字段默认 final:所有字段在编译后自动标记为 final
无 Setter 方法:不会生成修改字段的方法
类为 final:禁止继承,防止子类破坏不可变性
深拷贝保护:构造函数直接赋值,不暴露内部引用
2. 代码验证不可变性
(1) 尝试直接修改字段(编译错误)
public record Point(int x, int y) {}
public class Main {
public static void main(String[] args) {
Point p = new Point(10, 20);
p.x = 30; // 编译错误:Cannot assign a value to final variable 'x'
}
}
(2) 反射强制修改字段(运行时异常)
import java.lang.reflect.Field;
public class ReflectionTest {
public static void main(String[] args) throws Exception {
Point p = new Point(10, 20);
Field xField = Point.class.getDeclaredField("x");
xField.setAccessible(true);
xField.set(p, 100); // 抛出 IllegalAccessException
}
}
输出:
java.lang.IllegalAccessException: Can not set final int field Point.x to java.lang.Integer
(3) 引用类型字段的保护
import java.util.Collections;
import java.util.List;
public record Student(String name, List<String> courses) {
// 防御性拷贝:返回不可修改的集合
public List<String> courses() {
return Collections.unmodifiableList(courses);
}
}
public class Main {
public static void main(String[] args) {
List<String> courses = new ArrayList<>(List.of("Math", "Physics"));
Student s = new Student("Alice", courses);
// 尝试修改外部集合(不影响 record 内部)
courses.add("Chemistry");
System.out.println(s.courses()); // 输出:[Math, Physics]
// 尝试通过返回的引用修改(抛出异常)
s.courses().add("Biology"); // UnsupportedOperationException
}
}
3. 对比传统不可变类
传统实现需要手动保证不可变性:
public final class ManualPoint {
private final int x;
private final int y;
public ManualPoint(int x, int y) {
this.x = x;
this.y = y;
}
// 必须手动编写 Getter
public int x() { return x; }
public int y() { return y; }
// 必须重写 equals/hashCode/toString
@Override
public boolean equals(Object o) { /* 手动实现 */ }
@Override
public int hashCode() { /* 手动实现 */ }
@Override
public String toString() { /* 手动实现 */ }
}
record 优势:自动生成所有样板代码,减少人为错误。
4. 不可变性的实际应用场景
(1) 线程安全共享数据
public record Configuration(String apiKey, int timeout) {}
// 多线程安全共享
Configuration config = new Configuration("secret-key", 5000);
Runnable task = () -> {
System.out.println(config.apiKey()); // 无需同步
};
(2) 哈希表键值
public record Coordinate(int x, int y) {}
Map<Coordinate, String> map = new HashMap<>();
map.put(new Coordinate(1, 2), "Treasure");
// 可靠的哈希计算和相等性判断
(3) 函数式编程参数
public record Order(String id, BigDecimal amount) {}
List<Order> orders = ...;
BigDecimal total = orders.stream()
.map(Order::amount)
.reduce(BigDecimal.ZERO, BigDecimal::add);
总结
Java record 通过以下机制确保不可变性:
编译时强制:生成 final 字段和类
运行时保护:反射修改会抛出异常
API 设计:不暴露修改入口
引用控制:通过防御性拷贝保护引用类型字段
使用 record 能高效、安全地实现不可变数据载体类,适用于需要数据完整性、线程安全和函数式编程的场景。