如何在Java中创建不可变类?( 二 )


System.out.println("Alex age year before modification = " + student.getAge().getYear());
age.setYear(1993);
System.out.println("Alex age year after modification = " + student.getAge().getYear());
运行上面的测试后 , 我们得到以下输出:
Alex age year before modification = 1992
Alex age year after modification = 1993
我们声称ImmutableStudent是一个不可变的类 , 其状态在构造之后就不会修改 , 但是在上面的示例中 , 即使在构造Alex对象之后 , 我们也能够修改Alex的年龄 。如果我们返回ImmutableStudent构造函数的实现 , 则会发现age字段已分配给Age参数的实例 , 因此 , 只要在类外部修改了引用的Age , 该更改就会直接反映在Alex的状态上 。请查看我的“ 按价值传递”或“按参考传递”文章 , 以更深入地了解此概念 。
为了解决这个问题并使我们的类再次变得不可变 , 我们遵循上面提到的创建不可变类的步骤中的步骤5 。因此 , 我们修改了构造函数 , 以克隆传递的Age参数并使用其实例 。
public ImmutableStudent(int id, String name, Age age) {
Age cloneAge = new Age();
cloneAge.setDay(age.getDay());
cloneAge.setMonth(age.getMonth());
cloneAge.setYear(age.getYear());
this.age = cloneAge;
现在 , 如果运行测试 , 将得到以下输出:
Alex age year after modification = 1992
正如您现在所看到的 , Alex的年龄在构建之后再也不会受到影响 , 我们的班级又回到了不变的状态 。
【如何在Java中创建不可变类?】3.3从不可变类返回可变对象
但是 , 我们的类仍然有泄漏 , 并且不是完全不变的 , 让我们采取以下测试方案:
student.getAge().setYear(1993);
输出:
再次根据步骤4 , 从不可变对象返回可变字段时 , 您应该返回它们的克隆实例 , 而不是该字段的真实实例 。
因此 , 我们修改了getAge()以便返回该对象年龄的副本:
public Age getAge() {
cloneAge.setDay(this.age.getDay());
cloneAge.setMonth(this.age.getMonth());
cloneAge.setYear(this.age.getYear());
return cloneAge;
现在 , 该类变得完全不可变 , 并且没有为其他对象提供任何方法或方法来修改其状态 。
4.结论
不可变的类具有很多优点 , 尤其是在多线程环境中正确使用时 。唯一的缺点是它们比传统类消耗更多的内存 , 因为每次对其进行修改后 , 都会在内存中创建一个新对象...但是 , 与这些类提供的优点相比 , 开发人员不应高估内存消耗 , 因为它可以忽略不计类的类型 。
最后 , 如果一个对象仅能向其他对象呈现一种状态 , 则无论它们如何以及何时调用其方法 , 该对象都是不可变的 。如果是这样 , 则通过任何线程安全的定义都是线程安全的 。




推荐阅读