临时数据容器——匿名内部类(new Object)、HashMap、Record(不可变数据载体)

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 能高效、安全地实现不可变数据载体类,适用于需要数据完整性、线程安全和函数式编程的场景。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值