现在开始学习如何设计复杂应用程序所需要的各种主力类( workhorse class)。通常,这些类没有 main 方法, 却有自己的实例域和实例方法。 要想创建一个完整的程序, 应该将若干类组合在一起, 其中只有一个类有 main 方法。
自定义类
在Java中,最简单的类定义形式为:
class ClassName
{
field1
field2
...
constructor1
constructor2
...
method1
method2
}
在一个源文件中, 只能有一个公有类,但可以有任意数目的非公有类。源文件名必须与 public 类的名字相匹配。当编译含有多个类的源文件时,编译器将在目录下分别创建类文件。
多个源文件的使用
虽然一个源文件可以包含多个类,但许多程序员习惯于将每一个类存在一个单独的源文件中。例如,将 Employee 类存放在文件 Employee.java 中, 将 EmployeeTest 类存放在文件 EmployeeTest.java 中。
如果喜欢这样组织文件, 将可以有两种编译源程序的方法。一种是使用通配符调用 Java编译器:
javac Employee*.java
于是,所有与通配符匹配的源文件都将被编译成类文件。或者在windows运行窗口键入下列命令:
javac EmployeeTest.java
使用第二种方式,虽然没有显式地编译 Employee.java 。但是当 Java 编译器发现 EmployeeTest.java 使用了Employee 类时会查找名为 Employee.class 的文件。如果没有找到这个文件, 就会自动地搜索 Employee.java, 然后,对它进行编译。更重要的是:如果 Employee.java 版本较已有的 Employee.dass 文件版本新,Java 编译器就会自动地重新编译这个文件。
构造器
在构造类的对象时,构造器会运行,以便将实例域初始化为所希望的状态。构造器与其他的方法有一个重要的不同,构造器总是伴随着new操作符的执行被调用,而不能对一个已经存在的对象调用构造器来达到重新设置实例域的目的。构造器有多个特征,具体总结如下:
- 构造器与类同名
- 每个类可以有一个以上的构造器
- 构造器可以有0个,1个或多个参数
- 构造器没有返回值
- 构造器总是伴随着new操作一起调用
注意:不要在构造器中定义与实例域重名的局部变量
隐式参数与显式参数
方法用于操作对象以及存取它们的实例域。例如,方法:
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}
将调用这个方法的对象的 salary 实例域被设置为新值。看看下面这个调用:
number. raiseSalary(5);
它的结果将 number.salary 域的值增加 5%。具体地说,这个调用将执行下列指令:
double raise = number.salary * 5 / 100;
number.salary += raise;
raiseSalary 方法有两个参数。 第一个参数称为隐式 ( implicit ) 参数, 是出现在方法名前的Employee 类对象。第二个参数位于方法名后面括号中的数值,这是一个显式 ( explicit) 参 数 。可以看到,显式参数是明显地列在方法声明中的, 例如 double byPercent。隐式参数没有出现在方法声明中。在每一个方法中, 关键字 this 表示隐式参数。 如果需要的话,可以用下列方式编写raiseSalary 方法:
public void raiseSalary(double byPercent) {
double raise = this.salary * byPercent / 100;
this.salary += raise;
}
有些程序员更偏爱这样的风格,因为这样可以将实例域与局部变量明显地区分开来。
封装的优点
在有些时候,需要获得或设置实例域的值,因此,应该提供下面三项内容:
- 一个私有的数据域
- 一个公有的域访问器方法;
- 一个公有的域更该器方法。
这样做虽然比提供一个简单的公有数据域复杂些,但是却有着下列明显的好处:
- 首先可以改变内部实现,除了该类的方法之外,不会影响其他代码。
- 更改器方法可以执行错误检查,然而直接对域进行赋值将不会进行这些处理。
基于类的访问权限
从前面已经知道,方法可以访问所调用对象的私有数据。一个方法可以访问所属类的所有对象的私有数据,这令很多人感到奇怪!例如,下面看一下用来比较两个雇员的 equals 方法。
class Employee
{
public boolean equals(Employee other)
{
return name.equals(other.name);
}
}
典型的调用方式是
if (harry,equals(boss)) . . .
这个方法访问 harry 的私有域, 这点并不会让人奇怪,然而, 它还访问了 boss 的私有域。这是合法的, 其原因是 boss 是 Employee 类对象, 而== Employee 类的方法可以访问 Employee 类的任何一个对象的私有域==。
私有方法
在 Java 中,为了实现一个私有的方法, 只需将关键字 public 改为 private 即可。
final实例域
构建对象时必须初始化将实例域定义为 final的域。也就是说, 必须确保在每一个构造器执行之后,这个域的值被设置, 并且在后面的操作中, 不能够再对它进行修改。例如,可以将 Employee 类中的 name 域声明为 final, 因为在对象构建之后,这个值不会再被修改, 即没有 setName 方法。
class Employee
{
private final String name;
} …
final 修饰符大都应用于基本 (primitive ) 类型域,或不可变(immutable) 类的域(如果类中的每个方法都不会改变其对象, 这种类就是不可变的类。例如,String类就是一个不可变的类)。
对于可变的类, 使用 final 修饰符可能会对读者造成混乱。例如,
private final StringBuiIder evaluations;`
在 Employee 构造器中会初始化为
evaluations = new StringBuilder();
final 关键字只是表示存储在 evaluations 变量中的对象引用不会再指示其他 StringBuilder对象。不过这个对象可以更改:
public void giveGoldStar()
{
evaluations.append(LocalDate.now() + ": Gold star!\n");
}