面向对象特性——封装

封装的优点

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

public double getSalary()
{
    return salary;
}

public LocalDate getHireDay()
{
    return hireDay;
}

public void raiseSalary(double byPercent)
{
    double raise = salary * byPercent / 100;
    salary += raise;
}

这些都是典型的访问器方法。由于它们只返回实例字段值,因此又称之为字段访问器。

如果将name、salary和hireDay字段标记为公共,而不是编写单独的访问器方法,难道不是更容易一些吗?

name只是一个只读字段,一旦在构造器中设置,就没有任何方法可以对它进行修改,这样我们就可以确保name字段不会受到外界的破坏。

虽然salary不是只读字段,但是它只能用raiseSalary方法修改。特别是一旦知道这个值出现了错误,只要调试这个方法就可以了。如果salary是公共的,破坏这个子段值的捣乱者就可能出现在任何地方(那就很难调试了)。

有些时候,可能想要或者或设置字段值,那么需要下面三项内容:

  • 一个私有数据字段
  • 一个公共的字段访问器方法
  • 一个公共的字段修改器方法

这样做要比提供一个简单的公共数据字段复杂些,但却有着下列明显的好处:

首先,可以改变内部实现,而除了该类的方法以外,这不会影响其他代码。例如,如果将存储的字段修改为:

String firstName;
String lastName;

那么getName方法可以返回

firstName+" "+lastName

这个改变对于程序的其他部分是完全不可见的。

当然,为了进行新旧数据表示之间的转换,访问器方法和更改器方法可能需要做许多工作。但是,这将为我们带来第二点好处:更改器方法可以完成错误检查,而只对字段赋值的代码可能没有这个麻烦。比如,setSalary方法可以检查工资是否小于0.

注意不要返回可变对象引用的访问器方法,这样会破坏封装性。如果需要返回一个可变对象的引用,首先应该对它进行克隆,返回这个克隆的对象。

这里给出一个经验,如果需要返回一个可变数组字段的副本,就应该使用clone

私有方法

通过将方法设计为私有,如果你改变了方法的实现方式,就没有义务保证这个方法依然可用。如果数据的表示发生了变化,这个方法可能变得难以实现,或者不再需要;这并不是重点。重点在于,只要方法是私有的,类的设计者就可以确信它不会再别处使用,所以可以将其删去。如果一个方法是公共的,就不能简单地将其删除,因为可能会有其他代码依赖这个方法。

可见性

java的可见性public/protect/private,有两个层次——类级别和字段、方法级别

类级别:

类级别 / public
同一个包内 ok ok
其他包 ok

字段、方法级别

字段、方法级别 private protect / public
同一个类 ok ok ok ok
子类 ok ok ok
同一个包的类 ok ok
其他包的类 ok