并发设计模式实战系列(12):不变模式(Immutable Object)

简介: 🌟 大家好,我是摘星!🌟今天为大家带来的是并发设计模式实战系列,第十二章,废话不多说直接开始~

 

image.gif 编辑

🌟 大家好,我是摘星! 🌟

今天为大家带来的是并发设计模式实战系列,第十二章不变模式(Immutable Object),废话不多说直接开始~

目录

一、核心原理深度拆解

1. 不可变性的实现机制

2. 线程安全原理

二、生活化类比:博物馆展品

三、Java代码实现(生产级Demo)

1. 完整可运行代码

2. 关键实现技术

四、横向对比表格

1. 线程安全方案对比

2. JDK中的不可变类

五、高级优化技巧

1. 享元模式优化

2. 构建器模式

3. 不可变集合进阶

六、不可变对象的进阶特性

1. 伪不可变对象的陷阱

2. 不可变对象的性能优化

对象池化(Flyweight Pattern)

延迟哈希计算

七、工程实践中的模式变体

1. 部分不可变(Partial Immutability)

2. 建造者模式增强

八、与其他模式的协同应用

1. 不可变对象 + 观察者模式

2. 不可变集合 + 函数式编程

九、反模式与注意事项

1. 常见误用案例

2. 性能权衡场景

十、现代Java中的增强支持

1. Record类的本质不可变性

2. 不可变集合API演进


一、核心原理深度拆解

1. 不可变性的实现机制

┌───────────────────┐        ┌───────────────────┐
│   Mutable State    │        │  Immutable Object  │
│                   │        │                   │
│  - 可修改字段      │──克隆─>│  - final字段       │
│  - setter方法      │        │  - 无修改方法      │
└───────────────────┘        └───────────────────┘

image.gif

  • 构造时冻结:所有字段通过构造函数一次性初始化
  • 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; }
}

image.gif

  • 无状态变化:对象创建后内部状态永不改变
  • 无竞态条件:读操作不需要同步(因为数据不变)

二、生活化类比:博物馆展品

系统组件

现实类比

核心特性

可变对象

实验室中的文物

可修复、可清洁、需严格保护

不可变对象

展出的文物复制品

观众任意观察,无需防护措施

构造函数

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); // 输出显示不受影响
    }
}

image.gif

2. 关键实现技术

// 1. 防御性拷贝(针对可变字段)
this.hobbies = new ArrayList<>(hobbies); 
// 2. 不可修改视图
Collections.unmodifiableList(hobbies);
// 3. 深拷贝日期类
this.birthDate = new Date(birthDate.getTime());
// 4. final类禁止继承
public final class ImmutablePerson {...}

image.gif


四、横向对比表格

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; // 所有线程共享同一个安全实例
}

image.gif

2. 构建器模式

// 解决构造函数参数过多问题
public static class Builder {
    private String name;
    private int age;
    // ...其他字段
    
    public ImmutablePerson build() {
        return new ImmutablePerson(name, age, hobbies, birthDate);
    }
}

image.gif

3. 不可变集合进阶

// Java 9的工厂方法创建不可变集合
List<String> hobbies = List.of("Reading", "Hiking");
// Guava的不可变集合
ImmutableList<String> hobbies = 
    ImmutableList.<String>builder().add("Reading").add("Hiking").build();

image.gif


通过这种原理剖析+生活类比+生产代码+横向对比的解析方式,可以全面掌握不可变对象模式的设计精髓,并能在实际项目中正确应用。

好的!我们继续扩展内容,序号从 开始,深入探讨不可变对象模式的高级特性和工程实践:


六、不可变对象的进阶特性

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);  // 成功修改"不可变"对象

image.gif

解决方案

  • 所有可变字段进行深拷贝(如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));
    }
    // ...原有实现
}

image.gif

延迟哈希计算

// 适用于重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;
}

image.gif


七、工程实践中的模式变体

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++;
    }
}

image.gif

适用场景

需要跟踪对象访问次数但核心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;
    }
}

image.gif


八、与其他模式的协同应用

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);
    }
}

image.gif

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());

image.gif


九、反模式与注意事项

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());  // 自动生成访问方法

image.gif

2. 不可变集合API演进

Java版本

特性

示例

Java 8

Collections.unmodifiableX

Collections.unmodifiableList(list)

Java 9

List/Set/Map.of

List.of("a", "b")

Java 10

Collectors.toUnmodifiable

stream.collect(Collectors.toUnmodifiableList())

目录
打赏
0
0
0
0
18
分享
相关文章
并发设计模式实战系列(2):领导者/追随者模式
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发设计模式实战系列,第二章领导者/追随者(Leader/Followers)模式,废话不多说直接开始~
73 0
并发设计模式实战系列(1):半同步/半异步模式
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发设计模式实战系列,第一章半同步/半异步(Half-Sync/Half-Async)模式,废话不多说直接开始~
63 0
并发设计模式实战系列(4):线程池
需要建立持续的性能剖析(Profiling)和调优机制。通过以上十二个维度的系统化扩展,构建了一个从。设置合理队列容量/拒绝策略。动态扩容/优化任务处理速度。检查线程栈定位热点代码。调整最大用户进程数限制。CPU占用率100%
181 0
【实战指南】设计模式 - 工厂模式
工厂模式是一种面向对象设计模式,通过定义“工厂”来创建具体产品实例。它包含简单工厂、工厂方法和抽象工厂三种形式,分别适用于不同复杂度的场景。简单工厂便于理解但扩展性差;工厂方法符合开闭原则,适合单一类型产品创建;抽象工厂支持多类型产品创建,但不便于新增产品种类。三者各有优缺点,适用于不同设计需求。
设计模式觉醒系列(04)策略模式|简单工厂模式的升级版
本文介绍了简单工厂模式与策略模式的概念及其融合实践。简单工厂模式用于对象创建,通过隐藏实现细节简化代码;策略模式关注行为封装与切换,支持动态替换算法,增强灵活性。两者结合形成“策略工厂”,既简化对象创建又保持低耦合。文章通过支付案例演示了模式的应用,并强调实际开发中应根据需求选择合适的设计模式,避免生搬硬套。最后推荐了JVM调优、并发编程等技术专题,助力开发者提升技能。
并发设计模式实战系列(3):工作队列
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发设计模式实战系列,第三章,废话不多说直接开始~
46 0
【设计模式】【创建型模式】工厂方法模式(Factory Methods)
一、入门 什么是工厂方法模式? 工厂方法模式(Factory Method Pattern)是一种创建型设计模式,它定义了一个用于创建对象的接口,但由子类决定实例化哪个类。工厂方法模式使类的实例化延迟
91 16
前端必须掌握的设计模式——模板模式
模板模式(Template Pattern)是一种行为型设计模式,父类定义固定流程和步骤顺序,子类通过继承并重写特定方法实现具体步骤。适用于具有固定结构或流程的场景,如组装汽车、包装礼物等。举例来说,公司年会节目征集时,蜘蛛侠定义了歌曲的四个步骤:前奏、主歌、副歌、结尾。金刚狼和绿巨人根据此模板设计各自的表演内容。通过抽象类定义通用逻辑,子类实现个性化行为,从而减少重复代码。模板模式还支持钩子方法,允许跳过某些步骤,增加灵活性。
351 11
并发设计模式实战系列(20):扇出/扇入模式(Fan-Out/Fan-In)(完结篇)
🌟 大家好,我是摘星!🌟今天为大家带来的是并发设计模式实战系列,第二十章,废话不多说直接开始~
80 0
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式

热门文章

最新文章

AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等

登录插画

登录以查看您的控制台资源

管理云资源
状态一览
快捷访问