不可变对象(Immutable Object)的定义
不可变对象是指一旦创建后,其状态(即对象的数据)就不能被修改或改变的对象。这种对象具有一些重要的特性,如线程安全性、安全性、可重用性等。
Java中创建不可变对象的步骤
在Java中,创建不可变对象通常遵循以下步骤:
-
将类声明为
final
:- 这可以防止其他类通过继承来修改不可变对象的特性。
-
将所有字段声明为
private
和final
:private
修饰符确保字段只能在类内部访问,从而防止外部直接修改字段值。final
修饰符确保字段在对象创建后只能被赋值一次,从而确保字段的不可变性。
-
不提供修改字段值的方法:
- 不提供
setter
方法或其他可以修改字段值的方法。 - 只提供
getter
方法用于获取字段的值。
- 不提供
-
处理可变对象作为字段的情况:
- 如果类的字段是可变引用类型(如集合、自定义对象等),则需要采取额外的措施来确保不可变性。
- 在可能的情况下,使用不可变对象作为字段。
- 如果必须使用可变对象,则在构造函数或方法中进行防御性复制,以避免通过外部引用修改对象状态。
-
确保对象在构造完成之前不会泄露引用:
- 在构造函数中,确保在对象完全构造完成之前不会泄露对对象的引用。
- 这可以通过在构造函数中使用局部变量并在最后一步将对象引用赋值给类字段来实现。
示例代码
以下是一个创建不可变对象的示例代码:
public final class ImmutablePerson { |
|
private final String name; |
|
private final int age; |
|
private final List<String> hobbies; |
|
// 构造函数 |
|
public ImmutablePerson(String name, int age, List<String> hobbies) { |
|
this.name = name; |
|
this.age = age; |
|
// 对可变对象进行防御性复制 |
|
this.hobbies = new ArrayList<>(hobbies); |
|
} |
|
// Getter方法 |
|
public String getName() { |
|
return name; |
|
} |
|
public int getAge() { |
|
return age; |
|
} |
|
public List<String> getHobbies() { |
|
// 返回副本以避免外部修改 |
|
return new ArrayList<>(hobbies); |
|
} |
|
// 重写toString方法以便于打印输出 |
|
@Override |
|
public String toString() { |
|
return "ImmutablePerson{" + |
|
"name='" + name + '\'' + |
|
", age=" + age + |
|
", hobbies=" + hobbies + |
|
'}'; |
|
} |
|
// 主方法用于测试 |
|
public static void main(String[] args) { |
|
List<String> hobbies = new ArrayList<>(); |
|
hobbies.add("Reading"); |
|
hobbies.add("Swimming"); |
|
ImmutablePerson person = new ImmutablePerson("Alice", 30, hobbies); |
|
// 打印输出不可变对象 |
|
System.out.println(person); |
|
// 尝试修改不可变对象(这将导致编译错误) |
|
// person.setName("Bob"); // 编译错误:没有setName方法 |
|
// 尝试通过getter方法返回的副本修改原始对象(这不会影响原始对象) |
|
List<String> personHobbies = person.getHobbies(); |
|
personHobbies.add("Running"); |
|
System.out.println(person); // 输出不会显示添加的"Running" |
|
} |
|
} |
在这个示例中,ImmutablePerson
类是一个不可变对象。它的字段name
、age
和hobbies
都被声明为private
和final
,并且没有提供修改这些字段的setter
方法。对于可变对象hobbies
,在构造函数中进行了防御性复制,并且在getHobbies
方法中返回了副本以避免外部修改。这样,一旦创建了ImmutablePerson
对象,其状态就保持不变。