编辑
🌟 大家好,我是摘星! 🌟
今天为大家带来的是并发设计模式实战系列,第十二章不变模式(Immutable Object),废话不多说直接开始~
目录
1. 部分不可变(Partial Immutability)
一、核心原理深度拆解
1. 不可变性的实现机制
┌───────────────────┐ ┌───────────────────┐ │ Mutable State │ │ Immutable Object │ │ │ │ │ │ - 可修改字段 │──克隆─>│ - final字段 │ │ - setter方法 │ │ - 无修改方法 │ └───────────────────┘ └───────────────────┘
- 构造时冻结:所有字段通过构造函数一次性初始化
- final双重保障:
- 编译期:final字段禁止重新赋值
- 运行期:反射修改final字段会抛IllegalAccessException
2. 线程安全原理
// 典型不可变类示例 public final class ImmutablePoint { private final int x; private final int y; // 唯一初始化机会 public ImmutablePoint(int x, int y) { this.x = x; this.y = y; } // 只有getter没有setter public int getX() { return x; } public int getY() { return y; } }
- 无状态变化:对象创建后内部状态永不改变
- 无竞态条件:读操作不需要同步(因为数据不变)
二、生活化类比:博物馆展品
系统组件 |
现实类比 |
核心特性 |
可变对象 |
实验室中的文物 |
可修复、可清洁、需严格保护 |
不可变对象 |
展出的文物复制品 |
观众任意观察,无需防护措施 |
构造函数 |
3D扫描复制过程 |
一次性完成精确复制 |
- 安全优势:观众(线程)可以随意查看(读取)展品,无需担心损坏(数据竞争)
三、Java代码实现(生产级Demo)
1. 完整可运行代码
import java.util.*; // 深度不可变类示例 public final class ImmutablePerson { private final String name; private final int age; private final List<String> hobbies; private final Date birthDate; // 防御性拷贝构造函数 public ImmutablePerson(String name, int age, List<String> hobbies, Date birthDate) { this.name = name; this.age = age; this.hobbies = Collections.unmodifiableList(new ArrayList<>(hobbies)); this.birthDate = new Date(birthDate.getTime()); // Date是可变的,必须拷贝 } // 只有getter方法 public String getName() { return name; } public int getAge() { return age; } // 返回不可修改视图 public List<String> getHobbies() { return hobbies; } // 返回防御性拷贝 public Date getBirthDate() { return new Date(birthDate.getTime()); } @Override public String toString() { return "Person[name=" + name + ", age=" + age + ", hobbies=" + hobbies + ", birth=" + birthDate + "]"; } public static void main(String[] args) { List<String> hobbies = Arrays.asList("Reading", "Hiking"); Date birth = new Date(90, 5, 15); // 1990-06-15 ImmutablePerson person = new ImmutablePerson("Alice", 30, hobbies, birth); // 尝试修改原始数据 hobbies.set(0, "Gaming"); birth.setYear(95); System.out.println(person); // 输出显示不受影响 } }
2. 关键实现技术
// 1. 防御性拷贝(针对可变字段) this.hobbies = new ArrayList<>(hobbies); // 2. 不可修改视图 Collections.unmodifiableList(hobbies); // 3. 深拷贝日期类 this.birthDate = new Date(birthDate.getTime()); // 4. final类禁止继承 public final class ImmutablePerson {...}
四、横向对比表格
1. 线程安全方案对比
方案 |
线程安全原理 |
性能开销 |
适用场景 |
synchronized |
互斥访问 |
高 |
高竞争写操作 |
volatile |
可见性保证 |
低 |
单一状态变更 |
Immutable Object |
无状态变化 |
无 |
高频读取、配置类数据 |
CopyOnWrite |
写时复制 |
中 |
读多写少集合 |
2. JDK中的不可变类
类名 |
可变字段处理方式 |
典型用途 |
String |
内部char数组为final |
文本处理 |
Integer |
final int value |
数值包装 |
LocalDateTime |
所有字段final |
日期时间处理 |
Collections.emptyList() |
无修改方法 |
空集合占位 |
五、高级优化技巧
1. 享元模式优化
// 缓存常用实例(如IntegerCache) private static final ImmutablePerson DEFAULT_USER = new ImmutablePerson("Guest", 0, List.of(), new Date(0)); public static ImmutablePerson defaultUser() { return DEFAULT_USER; // 所有线程共享同一个安全实例 }
2. 构建器模式
// 解决构造函数参数过多问题 public static class Builder { private String name; private int age; // ...其他字段 public ImmutablePerson build() { return new ImmutablePerson(name, age, hobbies, birthDate); } }
3. 不可变集合进阶
// Java 9的工厂方法创建不可变集合 List<String> hobbies = List.of("Reading", "Hiking"); // Guava的不可变集合 ImmutableList<String> hobbies = ImmutableList.<String>builder().add("Reading").add("Hiking").build();
通过这种原理剖析+生活类比+生产代码+横向对比的解析方式,可以全面掌握不可变对象模式的设计精髓,并能在实际项目中正确应用。
好的!我们继续扩展内容,序号从 六 开始,深入探讨不可变对象模式的高级特性和工程实践:
六、不可变对象的进阶特性
1. 伪不可变对象的陷阱
// 表面不可变但实际可变的危险案例 public final class DangerousImmutable { private final Date date; // Date本身是可变的 public DangerousImmutable(Date date) { this.date = date; // 错误:未做防御性拷贝 } public Date getDate() { return date; // 错误:直接返回可变引用 } } // 攻击方式: DangerousImmutable obj = new DangerousImmutable(new Date()); obj.getDate().setYear(2025); // 成功修改"不可变"对象
解决方案:
- 对所有可变字段进行深拷贝(如
new Date(date.getTime())
) - 返回不可修改视图或拷贝副本
2. 不可变对象的性能优化
对象池化(Flyweight Pattern)
// 缓存常用值对象(适用于小而高频创建的对象) public class ImmutablePoint { private static final Map<String, ImmutablePoint> CACHE = new ConcurrentHashMap<>(); public static ImmutablePoint valueOf(int x, int y) { String key = x + "," + y; return CACHE.computeIfAbsent(key, k -> new ImmutablePoint(x, y)); } // ...原有实现 }
延迟哈希计算
// 适用于重hashCode()的对象 private volatile int hashCode; // 注意用volatile保证可见性 @Override public int hashCode() { if (hashCode == 0) { int result = 17; result = 31 * result + x; result = 31 * result + y; hashCode = result; } return hashCode; }
七、工程实践中的模式变体
1. 部分不可变(Partial Immutability)
// 部分字段可变的"半不可变"设计(需线程安全) public class SemiImmutable { private final String id; // 永久不可变 private volatile int counter; // 可变但线程安全 public SemiImmutable(String id) { this.id = id; } // 仅允许原子更新 public void increment() { counter++; } }
适用场景:
需要跟踪对象访问次数但核心ID不变的场景
2. 建造者模式增强
// 解决多参数构造问题(带校验) public class ImmutableConfig { private final String host; private final int port; // ...其他字段 public static class Builder { private String host; private int port; public Builder host(String host) { if (!host.matches("\\d+\\.\\d+\\.\\d+\\.\\d+")) { throw new IllegalArgumentException("Invalid host"); } this.host = host; return this; } public ImmutableConfig build() { return new ImmutableConfig(this); } } private ImmutableConfig(Builder builder) { this.host = builder.host; this.port = builder.port; } }
八、与其他模式的协同应用
1. 不可变对象 + 观察者模式
// 状态变更时生成新对象并通知观察者 public class ImmutableSensorData { private final double value; private final List<Consumer<ImmutableSensorData>> listeners = new CopyOnWriteArrayList<>(); public ImmutableSensorData update(double newValue) { ImmutableSensorData newData = new ImmutableSensorData(newValue); listeners.forEach(l -> l.accept(newData)); return newData; } public void addListener(Consumer<ImmutableSensorData> listener) { listeners.add(listener); } }
2. 不可变集合 + 函数式编程
List<ImmutablePerson> people = List.of( new ImmutablePerson("Alice", 30), new ImmutablePerson("Bob", 25) ); // 纯函数式处理 List<String> names = people.stream() .filter(p -> p.getAge() > 28) .map(ImmutablePerson::getName) .collect(Collectors.toUnmodifiableList());
九、反模式与注意事项
1. 常见误用案例
反模式 |
问题描述 |
正确做法 |
返回可变对象引用 |
外部可修改内部状态 |
返回防御性拷贝 |
继承不可变类 |
子类可能破坏不可变性 |
使用final类 |
构造函数参数校验缺失 |
可能构造出无效对象 |
构造时严格校验 |
2. 性能权衡场景
- 不适合场景:
- 高频更新的计数器(如AtomicLong更合适)
- 大规模对象图的频繁修改(考虑Copy-On-Write)
- 推荐场景:
- 配置信息
- 领域值对象(如Money、Color)
- 并发集合的键对象
十、现代Java中的增强支持
1. Record类的本质不可变性
// Java 14+ record自动实现不可变 public record ImmutablePoint(int x, int y) { // 编译器自动生成: // 1. final字段 // 2. 规范构造函数 // 3. equals/hashCode/toString } // 使用示例 ImmutablePoint p = new ImmutablePoint(10, 20); System.out.println(p.x()); // 自动生成访问方法
2. 不可变集合API演进
Java版本 |
特性 |
示例 |
Java 8 |
Collections.unmodifiableX |
|
Java 9 |
List/Set/Map.of |
|
Java 10 |
Collectors.toUnmodifiable |
|