不可变对象需同时满足:类为final、所有字段private final、无修改内部状态的方法;final仅保证引用不变,不阻止对象状态改变;适合值对象、配置类和HashMap键,但高频率修改场景需权衡GC开销。
不可变对象必须满足的三个条件
一个 Java 对象要被认定为不可变,不是靠加个
关键字就完事的。它得同时满足:类本身用
修饰(防止被继承)、所有字段都是
、并且没有暴露可修改内部状态的方法(比如不提供 setter,也不返回可变对象的引用)。漏掉任意一条,都可能被绕过防护。
是典型不可变类:它的
字段是
,且所有 public 方法(如
)都返回新对象,不修改原数组
如果字段是
,但通过 getter 返回了原始
引用,外部就能调用
修改内容——这不算真正不可变
构造器里若直接赋值传入的可变对象(如
),必须做防御性拷贝:
为什么
修饰引用不等于对象不可变
只保证该变量不能再指向别的对象,但不阻止对象自身状态被修改。这是最常被误解的一点。
上面代码中,
是
,但
本身是可变的。正确做法是用
包装,或在构造时深拷贝。
可变对象在并发场景下的典型问题
多线程共享可变对象时,如果没有
同步机制
,会出现可见性、原子性、重排序三类问题。比如
就是典型的非线程安全可变类。
Eclipse导入Android或其他的JAVA项目的正确方法 WORD版
本文档主要讲述的是Eclipse导入Android或其他的JAVA项目的正确方法;希望本文档会给有需要的朋友带来帮助;感兴趣的朋友可以过来看看
下载
立即学习
“
Java免费学习笔记(深入)
”;
多个线程共用同一个
实例调用
,可能抛出
或返回错误日期
解决方式不是加
(性能差),而是改用
(不可变、线程安全),或每次新建
实例
使用
也可行,但要注意内存泄漏风险(尤其在线程池中)
什么时候该设计成不可变对象
不是所有对象都适合不可变。它最适合那些状态一旦创建就不应改变、且会被多处共享的场景。
值对象(value object):如
、
、自定义的
、
配置类:如果整个应用生命周期内配置只读,用不可变对象能避免意外篡改
作为
的 key:不可变保证了
值稳定,不会因字段变化导致 key “消失”
但频繁修改的业务实体(如
订单状态流转)强行不可变会导致大量临时对象,GC 压力上升
不可变性的代价是每次“修改”都要新建对象,所以关键看修改频次和对象大小。小而稳的数据结构值得不可变;大而频繁变更的状态,得权衡清楚。
finalfinalprivate finalStringvalueprivate final char[]substring()private final ListArrayListadd()this.data = data;this.data = new ArrayList(data);finalfinalpublic class Person {
private final List hobbies;
public Person(List h) {
this.hobbies = h; // ❌ 危险:hobbies 指向外部传入的 ArrayList
}
public List getHobbies() {
return hobbies; // ✅ 外部拿到后可直接 add/remove
}
} hobbiesfinalArrayListCollections.unmodifiableList()SimpleDateFormatSimpleDateFormatparse()java.lang.NumberFormatExceptionsynchronizedDateTimeFormatterSimpleDateFormatThreadLocalLocalDateBigIntegerMoneyRangeHashMaphashCode()User