在设计类时,将字段设为不可变(immutable)可提升代码的健壮性和线程安全性。以下是修复可变字段为不可变的建议和步骤:
核心原则
-
无 Setter 方法:禁止提供修改字段的方法。
-
字段用
final修饰:强制在构造时初始化。 -
防御性拷贝:对引用类型字段,构造/返回时进行深拷贝。
-
类本身不可变:避免子类破坏不可变性(如将类声明为
final)。
修复步骤与示例
1. 基础类型字段
直接添加 final,移除 setter 方法:
java
// 修复前(可变)
public class User {
private int age; // 可变字段
public void setAge(int age) { this.age = age; }
}
// 修复后(不可变)
public final class User {
private final int age; // final 字段
public User(int age) { this.age = age; } // 构造时初始化
public int getAge() { return age; } // 无 setter
}
2. 引用类型字段(如数组、集合)
-
构造时深拷贝
-
Getter 返回不可修改视图/拷贝:
java
// 修复前(可变)
public class Data {
private List<String> items; // 可变集合
public void setItems(List<String> items) { this.items = items; }
}
// 修复后(不可变)
public final class Data {
private final List<String> items;
public Data(List<String> items) {
this.items = new ArrayList<>(items); // 深拷贝传入集合
}
public List<String> getItems() {
return Collections.unmodifiableList(items); // 返回只读视图
// 或返回深拷贝:return new ArrayList<>(items);
}
}
3. 自定义对象字段
确保引用的对象本身不可变:
java
public final class Address { // 被引用的类也需不可变
private final String city;
public Address(String city) { this.city = city; }
public String getCity() { return city; }
}
public final class User {
private final Address address; // 引用不可变对象
public User(Address address) {
this.address = new Address(address.getCity()); // 深拷贝
}
public Address getAddress() {
return new Address(address.getCity()); // 返回拷贝
}
}
4. 避免外部修改(防御性编程)
-
如果字段是数组:
java
public final class ImmutableArray { private final int[] array; public ImmutableArray(int[] array) { this.array = Arrays.copyOf(array, array.length); // 深拷贝 } public int[] getArray() { return Arrays.copyOf(array, array.length); // 返回拷贝 } }
关键检查点
-
✅ 所有字段用
final声明。 -
✅ 无 setter 方法。
-
✅ 引用类型在构造时深拷贝外部数据。
-
✅ Getter 返回只读视图或深拷贝。
-
✅ 类本身为
final(防止子类覆盖方法破坏不可变性)。
不可变类的优势
-
线程安全:无需同步,天然线程安全。
-
易于维护:状态在构造后永不改变。
-
安全共享:可自由缓存、重用对象(如
String)。
注意事项
-
深拷贝可能影响性能,需权衡场景。
-
对复杂嵌套对象,确保整个引用链不可变。
-
使用不可变集合库(如 Guava
ImmutableList)简化实现。

被折叠的 条评论
为什么被折叠?



