🌟 大家好,我是摘星! 🌟
今天为大家带来的是并发设计模式实战系列,第十二章不变模式(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 |
|