java 核心技术-12版 卷Ⅰ- 4.3.8 封装的优点
原文:
最后再仔细看一下非常简单的 getName 方法、getSalary方法和getHireDay方法。
这些都是典型的访问器方法。由于它们只返回字段的值,因此又被成为字段访问器(field accessor)。
如果将name 、salary 和 hireDay字段标记为公共,而不是编写单独的访问器方法,不是更容易一些吗?
不过,name 是一个只读字段,一旦在构造器中设置,就没有办法能够修改这个字段。这样我们可以确保name字段不会受到外界的破坏。
虽然salary 不是只读字段,但是它只能用raiseSalary方法修改。具体地,如果这个值出现了错误,那么只需要调试这个raiseSalary方法就可以了。如果salary字段是公共的,破坏这个字段值的罪魁祸首有可能出没在任何地方(那就很难调试了)。
有些时候,可能想要获得或设置实例字段的值,那么你需要提供下面三项内容:
一个私有的实例字段;
一个公共的字段访问器方法;
一个公共的字段更改器方法。
这样做要比提供一个简单的公共实例字段复杂些,但有很多明显的好处。
首先,可以改变内部实现,而不影响该类方法之外的任何其他代码。例如,如果将存储姓名的字段改为:
那么getName方法可以改为返回
这个修改对于程序的其余部分是完全不可见的。
当然,为了进行新旧数据表示之间的转换,访问器方法和更改器方法可能需要做许多工作。这将为为我们带来第二点好处:更改器方法可以完成错误检查,而只对字段赋值的代码不会费心这么做。例如,setSalary方法可以检查工资是否小于0.
警告:注意不要编写返回可变对象引用的访问器方法。在本书之前的一版中,我们的Employee类就违反了这个设计原则,其中getHireDay方法返回了一个Date对象:
LocalDate类没有更改器方法,与之不同,Date类有一个更改器方法setTime,可以设置毫秒数。
Date 对象是可变的,这一点破坏了封装性。如下:
出错的原因很微妙。d 和 em2 的hireDay 指向了同一个对象。因此,对d修改的时候, hireDay 也受到了影响。可以回忆一下之前咱们提到的对象引用的相关内容

如果需要返回一个可变对象的引用,首先应该对它进行克隆(clone)。对象克隆是指存放在另一个新位置上的对象副本。有关对象克隆的详细内容将在第6章中进行讨论。下面是修改后的代码:
这里有一个经验,如果需要返回一个可变字段的副本,就应该使用clone

个人建议
对于这个代码,用clone 也好,用其他方式也好,总之,要让return 的可变对象不能与封装起来的信息指向同一个对象,否则对象的指针将被泄漏,就像你的密码被泄漏一样危险。同理,其实本代码中,构造器那里也是一样防止泄漏。
具体到Date 类,也可以用以下方式获得Date 的同值,不同指向的新实例。这种方式不需要强转,会更加友好

