不可变类字段修复建议
在设计类时,将字段设为不可变(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
)简化实现。