最后,再仔细地看一下非常简单的getName方法、getSalary方法和getHireDay方法。
- public String getName()
- {
- return name;
- }
- public double getSalary()
- {
- return salary;
- }
- public Date getHireDay()
- {
- return hireDay;
- }
这些都是典型的访问器方法。由于它们只返回实例域值,因此又被称为域访问器。
将name、salary和hireDay域标记为public,以此来取代独立的访问器方法会不会更容易些呢?
关键在于name是一个只读域。一旦在构造器中设置完毕,就没有任何一个办法可以对它进行修改,这样来确保name域不会受到外界的干扰。
虽然salary不是只读域,但是它只能用raiseSalary方法修改。特别是一旦这个域值出现了错误,只要调试这个方法就可以了。如果salary域是public的,破坏这个域值的捣乱者有可能会出没在任何地方。
在有些时候,需要获得或设置实例域的值。因此,应该提供下面三项内容:
- 一个私有的数据域;
- 一个公有的域访问器方法;
- 一个公有的域更改器方法。
这样做要比提供一个简单的公有数据域复杂些,但是却有着下列明显的好处:
1)可以改变内部实现,除了该类的方法之外,不会影响其他代码。
例如,如果将存储名字域改为:
String firstName;
String lastName;
那么getName方法可以改为返回
firstName + “ ” + lastName
对于这点改变,程序的其他部分完全不可见。
当然,为了进行新旧数据表示之间的转换,访问器方法和更改器方法有可能需要做许多工作。但是,这将为我们带来了第二点好处。
2)更改器方法可以执行错误检查,然而直接对域进行赋值将不会进行这些处理。
例如,setSalary方法可以检查薪金是否小于0.
警告:注意不要编写返回引用可变对象的访问器方法。在Employee类中就违反了这个设计原则,其中的getHireDay方法返回了一个Date类对象。
- class Employee
- {
- ...
- public Date getHireDay()
- {
- return hireDay;
- }
- ...
- private Date hireDay;
- }
这样会破坏封装性!请看下面这段代码:
- Employee harry = ...;
- Date d = harry.getHireDay();
- double tenYearsInMilliSeconds = 10 * 365.25 * 24 * 60 * 60 * 1000;
- d.setTime(d.getTime() - (long)tenYearsInMilliSeconds);
- //let's give Harry ten years added seniority
出错的原因很微妙。d和harry.hireDay引用同一个对象(请参见图4-5)。对d调用更改器方法就可以自动地改变这个雇员对象的私有状态!
如果需要返回一个可变对象的引用,应该首先对它进行克隆(clone)。对象克隆是指存放在另一个位置上的对象副本。有关对象克隆的详细内容将在第6章中讨论。下面是修改后的代码:
- class Employee
- {
- ...
- public Date getHireDay()
- {
- return (Date)hireDay.clone();
- }
- ....
- }
凭经验可知,如果需要返回一个可变数据域的拷贝,就应该使用克隆。