java里面的可变类和不可变类

在 Java 中,可变类型(Mutable)和 不可变类型(Immutable)的设计与内存管理、性能优化密切相关。下面通过 字符串(String) 的核心机制和示例代码,详细解释它们的区别、原理及实际应用。


一、不可变类型(Immutable)的核心特性

不可变对象一旦创建,其状态(内容)不可被修改。任何“修改”操作都会生成新对象。

1. 不可变类的设计原则
  • 所有字段声明为 finalprivate:防止外部修改。
  • 不提供修改状态的 setter 方法:只能通过构造函数初始化。
  • 防御性拷贝(Defensive Copy):若类包含可变对象(如 Date),返回它们的深拷贝副本。
  • 类声明为 final:防止子类重写方法破坏不可变性。
2. Java 中常见的不可变类型
  • String:最经典的不可变类。
  • 基本类型的包装类:如 Integer, Double, Boolean
  • 日期时间类:如 LocalDateTime(Java 8+)。
  • BigIntegerBigDecimal:高精度计算类。

二、可变类型(Mutable)的特性

可变对象允许直接修改内部状态,无需创建新对象。例如:

  • StringBuilder / StringBuffer:可变的字符序列。
  • 集合类ArrayList, HashMap 等。
  • 自定义类:包含 setter 方法的类。

三、字符串(String)的不可变性与内存机制

1. 字符串的两种创建方式
// 方式1:字面量赋值 → 触发常量池机制
String s1 = "hello";
String s2 = "hello";

// 方式2:new 关键字 → 强制在堆中创建新对象
String s3 = new String("hello");
String s4 = new String("hello");
2. 字符串比较的底层原理
  • == 比较对象地址
    System.out.println(s1 == s2); // true(常量池复用)
    System.out.println(s3 == s4); // false(堆中不同对象)
    
  • equals() 比较内容
    System.out.println(s1.equals(s3)); // true(内容相同)
    
3. 字符串常量池(String Pool)
  • 位置:方法区(Java 7 前在永久代,Java 7+ 移至堆内存)。
  • 机制
    • 使用字面量(如 "hello")创建字符串时,JVM 检查常量池是否存在该字符串。
    • 若存在,直接返回池中对象的引用;若不存在,创建新对象并放入池中。
  • new String() 绕过常量池
    • 无论常量池是否存在该字符串,new 都会在堆中创建新对象。
4. intern() 方法:强制使用常量池
String s5 = new String("hello").intern();
String s6 = "hello";
System.out.println(s5 == s6); // true(s5 被放入常量池)

四、不可变类型的设计优势

1. 线程安全
  • 不可变对象天然线程安全,无需同步(如 String 在多线程中无需加锁)。
2. 缓存优化
  • 字符串常量池:减少重复字符串的内存开销。
  • 包装类缓存:如 Integer 缓存 -128~127 的数值。
3. 哈希键的稳定性
  • 不可变对象的哈希值不会改变,适合作为 HashMap 的键。

五、可变类型的适用场景

1. 需要频繁修改内容
  • StringBuilder:单线程下高效修改字符串。
  • StringBuffer:线程安全但性能较低(方法用 synchronized 修饰)。
2. 动态数据结构
  • 集合类:如 ArrayList.add(), HashMap.put() 直接修改内部数据。

六、字符串内存模型图解

String s1 = "hello";        // 常量池中创建对象
String s2 = "hello";        // 复用常量池对象
String s3 = new String("hello"); // 堆中新对象
String s4 = s3.intern();    // 返回常量池引用

七、不可变类的实现示例

自定义不可变类 ImmutablePerson
public final class ImmutablePerson {
    private final String name;
    private final int age;
    private final Date birthDate; // Date 是可变对象!

    public ImmutablePerson(String name, int age, Date birthDate) {
        this.name = name;
        this.age = age;
        // 防御性拷贝:防止外部修改影响内部状态
        this.birthDate = new Date(birthDate.getTime());
    }

    public Date getBirthDate() {
        // 返回拷贝,避免内部数据被修改
        return new Date(birthDate.getTime());
    }
}

八、总结:可变与不可变的核心对比

特性不可变类型(如 String可变类型(如 StringBuilder
内容修改创建新对象,原对象不变直接修改原对象
线程安全天然安全需手动同步(如 StringBuffer
内存优化常量池、缓存机制无特殊优化
适用场景多线程、哈希键、配置类单线程高频修改、动态集合操作

掌握可变与不可变类型的区别,能帮助你写出更高效、健壮的代码。例如:

  • 优先使用不可变对象:除非有明确的修改需求。
  • 避免共享可变对象:防止多线程竞争或意外修改。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值