最后,再仔细地看一下非常简单的getName方法、getSalary方法和getHireDay方法。

  1. public String getName() 
  2.     return name; 
  3. public double getSalary() 
  4.     return salary; 
  5. public Date getHireDay() 
  6.     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类对象。

  1. class Employee 
  2.     ... 
  3.     public Date getHireDay() 
  4.     { 
  5.         return hireDay; 
  6.     } 
  7.     ... 
  8.     private Date hireDay; 

这样会破坏封装性!请看下面这段代码:

  1. Employee harry = ...; 
  2. Date d = harry.getHireDay(); 
  3. double tenYearsInMilliSeconds = 10 * 365.25 * 24 * 60 * 60 * 1000
  4. d.setTime(d.getTime() - (long)tenYearsInMilliSeconds); 
  5. //let's give Harry ten years added seniority 

出错的原因很微妙。d和harry.hireDay引用同一个对象(请参见图4-5)。对d调用更改器方法就可以自动地改变这个雇员对象的私有状态!

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

  1. class Employee 
  2.     ... 
  3.     public Date getHireDay() 
  4.     { 
  5.         return (Date)hireDay.clone(); 
  6.     } 
  7.     .... 

凭经验可知,如果需要返回一个可变数据域的拷贝,就应该使用克隆。