继承
继承是面向对象编程最重要的优点之一。为了最有效地使用继承,正确了解继承非常重要。继承涉及以下重要概念:
extends 关键字 :继承在声明类时定义。使用 extends 关键字来指定您所编写的类的超类。
构造函数:构造函数不在子类中继承,但经常在子类构造函数中调用超类的构造函数。
重载/覆盖: 重载是编写名称相同但参数不同的几个方法。 覆盖是指更改子类中继承的方法的实现。
Object 类 :所有 Java 对象都最终继承自 Object 类,该类定义每个 Java 一定要具备的基本功能。
接口: 接口是一种行为的描述,但并不实现该行为。
扩展类
在 Visual Basic 中,类不能从任何其他类继承,但 Java 语言允许单继承。继承是重用代码的一种方式。当类 A 继承自(或 extends)类 B 时,类 A 就自动继承类 B 的所有 public 和 protected 成员。如果类 A 与类 B 在同一个包中,则类 A 还继承所有具有默认(或 包)访问权的成员。重要的是要注意,子类始终不会继承它们扩展的类的私有成员。
一旦您扩展了某个类,就可以添加新域和方法,这些新域和方法定义将新类与超类区别开来的属性和操作。另外,您可以 覆盖在子类中必须具有不同行为的超类的操作。
定义类时,可以显式扩展这个类。要扩展类,在该类名后跟一个 extends 关键字,然后是要扩展的类的名称。如果您没有显式扩展类,则 Java 编译器自动扩展类 Object 。这样,所有 Java 对象最终都是 Object 类的子类。
一个扩展例子
假定您要创建一个新的 CheckingAccount 类。 CheckingAccount 是一个特殊类型的 BankAccount 。换句话说, CheckingAccount 与 BankAccount 有相同的属性和操作。但是, CheckingAccount 还新增了一个操作――兑现支票。因此,您可以定义 CheckingAccount 类,使它扩展 BankAccount 并添加了一个 cashCheck() 方法,如清单 14 所示。
清单 14. 扩展类
2
3 public void cashCheck(float amount) {
4
5 withdraw(amount);
6
7 }
8
9 }
10
11
子类构造函数
构造函数不是真正的类成员,因而不对构造函数进行继承。 BankAccount 构造函数创建 BankAccount 对象,因此,不能在 CheckingAccount 类中使用它来创建 CheckingAccount 对象。但是,可以从超类中使用构造函数来初始化继承这个超类的子类的一部分。换句话说,您经常需要在子类构造函数中调用超类构造函数来对子类对象进行部分初始化。使用 super 关键字,后接代表要调用的超类构造函数的参数的参数化列表就可以做到这一点。如果您要在某个构造函数中使用 super 关键字来调用超类的构造函数,则它必须是构造函数体中的第一个语句。
例如,您需要编写 CheckingAccount 构造函数来初始化 CheckingAccount 对象。您要使用初始余额来创建 CheckingAccount 对象,因此您传入的是金额。这恰恰与 BankAccount 类中的构造函数非常类似,因此,您使用该构造函数来为您做所有这些工作,如清单 15 所示。
清单 15. 子类构造函数
2
3 public CheckingAccount(float balance) {
4
5 super(balance);
6
7 }
8
9 public void cashCheck(float amount) {
10
11 withdraw(amount);
12
13 }
14
15 }
16
17
您还可以使用 super 关键字来显式地在子类中引用超类的成员。
重载和覆盖
Java 语言允许定义几个同名的方法,只要这些方法的参数不同即可。例如,清单 16 又定义了一个 cashCheck() 方法,该方法带有两个参数,一个是要兑现的支票金额,一个是收取服务的费用。这就叫做方法 重载。
清单 16. 方法重载
2
3 withdraw(amount);
4
5 }
6
7 public void cashCheck(float amount, float fee) {
8
9 withdraw(amount+fee);
10
11 }
12
13
在创建子类时,您经常要 覆盖 从超类继承的方法的行为。例如,假定 CheckingAccount 和 BankAccount 之间的一个不同之处是从 CheckingAccount 提取金额时要收取费用。您需要在 CheckingAccount 类中覆盖该 withdraw() 方法,以便收取 0.25 美元费用。可以通过使用 super 关键字,根据 BankingAccount 的 withdraw() 方法来定义 CheckingAccount 的 withdraw() 方法,如清单 17 所示。
清单 17. 方法覆盖
2
3 super.withdraw(amount+0.25F);
4
5 }
6
7
Object 类
Object 类是 Java 类层次结构中的特殊类。所有 Java 类最终都是 Object 类的子类。换句话说,Java 语言支持集中式根类层次结构, Object class 类是该层次结构中的根类。Visual Basic 中也存在相似的概念, Object 变量以后可以实例化为任何类型的类。
由于所有 Java 对象都继承自 Object 类,所以,可以为任何 Java 对象调用在 Object 中定义的方法,获得类似的行为。例如,如果 Object 类定义了 toString() 方法,该方法返回代表该对象的 String 对象。您可以为任何 Java 对象调用 toString() 方法,获得该对象的字符串表示。大多数类定义都覆盖 toString() 方法,以便它可返回该特定类的特定字符串表示。
在 Java 类层次结构根部的 Object 还有一个含义,即所有对象都能向下强制类型转换(cast down)到 Object 对象。在 Java 语言中,您可以定义获得 Object 类的对象的数据结构,这些数据结构可以存放任何 Java 对象。
接口
我已经提过,Java 类只允许单继承,这意味着 Java 类只能扩展一个类。Java 语言的设计者感到多重继承太复杂,因此他们改为支持 接口。 接口类似于不能实例化的方法,它定义有方法,但实际上并不实现这些方法。
声明接口的方法与声明类相似,不同之处是使用 interface 关键字而非 class 关键字。接口可以扩展任意数量的超接口。接口内的方法不包含实现。接口方法只是简单的方法定义;它们没有方法体。这与 Visual Basic 使用的接口概念相同;接口由属性和方法声明组成,没有代码。
Account 接口
清单 18 中的代码展示如何为银行帐户编写定义一组功能的基本 Account 接口。注意,在接口中声明的方法没有方法体。
清单 18. Account 接口
2
3 public static final float INTEREST = 0.35F;
4
5 public void withdraw(float amount);
6
7 public void deposit(float amount);
8
9 }
10
11
实现接口
Java 类只可扩展一个类,但它可以 实现任意数量的接口。当类实现接口时,它必须实现该接口中定义个每个方法。
清单 19 定义了一个实现 Account 接口的 SavingsAccount 类。由于 Account 接口定义两个方法―― withdraw(float amount) 和 deposit(float amount) ,所以, SavingsAccount 类必须提供这两个类的实现。 SavingsAccount 类仍可以扩展另一个类,而且它可以实现任何其他接口,只要它们定义的成员与 Account 接口不同即可。
清单 19. 实现接口
2
3 private float balance;
4
5 public SavingsAccount(float balance) {
6
7 this.balance = balance;
8
9 }
10
11 public void cashCheck(float amount, float fee) {
12
13 withdraw(amount+fee);
14
15 }
16
17 public void withdraw(float amount) {
18
19 balance += balance;
20
21 }
22
23 public void deposit(float amount) {
24
25 balance -= balance;
26
27 }
28
29 }
30
31
小结
到此为止,您已经掌握了 Java 语言的基本组成,并能编写简单的 Java 程序。特别是,您应能:
编写带有 main() 方法的 Java 类,编译并运行它。
编写 Java 接口并编译它。
为您的类编写一个或多个构造函数。
编写扩展另一个类并实现一个或多个接口的类。
通过 new 关键字和构造函数调用来创建和使用对象。
您应有足够的信心来研究和编写更高级的 Java 代码。最好使用 Java 平台自带的类着手进行。获得使用这种语言经验的非常好的方法是浏览 API 文档,然后使用这些类开始编写程序。