1. 不可变对象(immutable object)的可测试性中天然线程安全、hash 稳定
请说明不可变对象(Immutable Object)的可测试性优势,包括天然线程安全与 hash 稳定?
- 不可变对象的定义与线程安全原理
- hash 稳定对集合/缓存行为的影响
- 不可变对象降低测试复杂度与不确定性
不可变对象创建后所有字段不可修改,因此天然线程安全——无需加锁即可被多线程共享,因为不存在可变的共享状态,也就不存在数据竞争。这使得针对不可变对象的测试确定性高:不用担心并发修改、缓存失效、状态被意外改变,测试结果稳定可复现。hash 稳定是另一大优势:由于对象状态不变,hashCode() 始终保持一致,因此它作为 HashMap/HashSet 的 key 时不会因状态变化导致哈希桶错乱、查找失败;作为缓存 key 时不会因内容变化使缓存失效。可测试性上,不可变对象让测试无需考虑 setter 副作用与状态迁移,构造即完整,断言直接基于构造参数;同时天然适合作为函数式处理、并发缓存、值对象(Value Object)的建模。实践上常通过 final 字段、private 构造、工厂方法、防御性拷贝(copy-on-write)实现不可变。测试要点:验证构造后状态不可变(尝试修改应失败或产生新对象)、验证 equals/hashCode 一致性、验证作为 key 的行为稳定。
不可变对象把"可变量"从设计中消除,从而把测试中的不确定性(并发、状态迁移、副作用)一并消除。线程安全是"结构性保证"而非"测试运气"——测试不必依赖加锁正确性。hash 稳定则让对象在集合/缓存中的行为可预测,测试自然稳定。不可变对象的可测试性本质是"更少的状态、更少的意外、更确定的断言"。
public final class Money {
private final BigDecimal amount;
private final String currency;
public Money(BigDecimal amount, String currency) {
this.amount = amount;
this.currency = currency;
}
public Money add(Money other) {
return new Money(amount.add(other.amount), currency); // 返回新对象
}
// equals/hashCode 基于 amount+currency
}
@Test
void immutableIsThreadSafeAndHashStable() {
Money m = new Money(new BigDecimal("100"), "CNY");
// 同一对象在多线程共享无副作用
Money sum = m.add(new Money(new BigDecimal("50"), "CNY"));
assertEquals(new BigDecimal("100"), m.getAmount()); // 原对象不变
assertEquals(m.hashCode(), m.hashCode()); // hash 稳定
}