按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!
System。out。println(〃toy = 〃 + toy);
System。out。println(〃castille = 〃 + castille);
}
public static void main(String'' args) {
Bath b = new Bath();
b。print();
}
} ///:~
请注意在Bath 构建器中,在所有初始化开始之前执行了一个语句。如果不在定义时进行初始化,仍然不能保
证能在将一条消息发给一个对象句柄之前会执行任何初始化——除非出现不可避免的运行期违例。
下面是该程序的输出:
Inside Bath()
Soap()
s1 = Happy
s2 = Happy
s3 = Joy
s4 = Joy
i = 47
toy = 3。14
castille = Constructed
调用print()时,它会填充 s4,使所有字段在使用之前都获得正确的初始化。
6。2 继承的语法
继承与Java (以及其他OOP 语言)非常紧密地结合在一起。我们早在第 1 章就为大家引入了继承的概念,并
在那章之后到本章之前的各章里不时用到,因为一些特殊的场合要求必须使用继承。除此以外,创建一个类
时肯定会进行继承,因为若非如此,会从Java 的标准根类 Object 中继承。
141
…………………………………………………………Page 143……………………………………………………………
用于合成的语法是非常简单且直观的。但为了进行继承,必须采用一种全然不同的形式。需要继承的时候,
我们会说:“这个新类和那个旧类差不多。”为了在代码里表面这一观念,需要给出类名。但在类主体的起
始花括号之前,需要放置一个关键字extends,在后面跟随“基础类”的名字。若采取这种做法,就可自动
获得基础类的所有数据成员以及方法。下面是一个例子:
//: Detergent。java
// Inheritance syntax & properties
class Cleanser {
private String s = new String(〃Cleanser〃);
public void append(String a) { s += a; }
public void dilute() { append(〃 dilute()〃); }
public void apply() { append(〃 apply()〃); }
public void scrub() { append(〃 scrub()〃); }
public void print() { System。out。println(s); }
public static void main(String'' args) {
Cleanser x = new Cleanser();
x。dilute(); x。apply(); x。scrub();
x。print();
}
}
public class Detergent extends Cleanser {
// Change a method:
public void scrub() {
append(〃 Detergent。scrub()〃);
super。scrub(); // Call base…class version
}
// Add methods to the interface:
public void foam() { append(〃 foam()〃); }
// Test the new class:
public static void main(String'' args) {
Detergent x = new Detergent();
x。dilute();
x。apply();
x。scrub();
x。foam();
x。print();
System。out。println(〃Testing base class:〃);
Cleanser。main(args);
}
} ///:~
这个例子向大家展示了大量特性。首先,在Cleanser append()方法里,字串同一个 s 连接起来。这是用
“+=”运算符实现的。同“+”一样,“+=”被Java 用于对字串进行“过载”处理。
其次,无论 Cleanser 还是Detergent 都包含了一个main()方法。我们可为自己的每个类都创建一个
main()。通常建议大家象这样进行编写代码,使自己的测试代码能够封装到类内。即便在程序中含有数量众
多的类,但对于在命令行请求的public 类,只有main()才会得到调用。所以在这种情况下,当我们使用
“java Detergent”的时候,调用的是Degergent。main()——即使Cleanser 并非一个public 类。采用这种
将main()置入每个类的做法,可方便地为每个类都进行单元测试。而且在完成测试以后,毋需将main()删
去;可把它保留下来,用于以后的测试。
在这里,大家可看到Deteregent。main()对 Cleanser。main()的调用是明确进行的。
142
…………………………………………………………Page 144……………………………………………………………
需要着重强调的是Cleanser 中的所有类都是public 属性。请记住,倘若省略所有访问指示符,则成员默认
为“友好的”。这样一来,就只允许对包成员进行访问。在这个包内,任何人都可使用那些没有访问指示符
的方法。例如,Detergent 将不会遇到任何麻烦。然而,假设来自另外某个包的类准备继承Cleanser ,它就
只能访问那些public 成员。所以在计划继承的时候,一个比较好的规则是将所有字段都设为private,并将
所有方法都设为public (protected 成员也允许衍生出来的类访问它;以后还会深入探讨这一问题)。当
然,在一些特殊的场合,我们仍然必须作出一些调整,但这并不是一个好的做法。
注意Cleanser 在它的接口中含有一系列方法:append(),dilute(),apply(),scrub()以及print()。由于
Detergent 是从Cleanser 衍生出来的(通过 extends 关键字),所以它会自动获得接口内的所有这些方法—
—即使我们在 Detergent 里并未看到对它们的明确定义。这样一来,就可将继承想象成“对接口的重复利
用”或者“接口的再生”(以后的实施细节可以自由设置,但那并非我们强调的重点)。
正如在 scrub()里看到的那样,可以获得在基础类里定义的一个方法,并对其进行修改。在这种情况下,我
们通常想在新版本里调用来自基础类的方法。但在 scrub()里,不可只是简单地发出对scrub()的调用。那样
便造成了递归调用,我们不愿看到这一情况。为解决这个问题,Java 提供了一个 super 关键字,它引用当前
类已从中继承的一个“超类”(Superclass)。所以表达式super。scrub()调用的是方法 scrub()的基础类版
本。
进行继承时,我们并不限于只能使用基础类的方法。亦可在衍生出来的类里加入自己的新方法。这时采取的
做法与在普通类里添加其他任何方法是完全一样的:只需简单地定义它即可。extends 关键字提醒我们准备
将新方法加入基础类的接口里,对其进行“扩展”。foam()便是这种做法的一个产物。
在Detergent。main()里,我们可看到对于Detergent 对象,可调用Cleanser 以及Detergent 内所有可用的
方法(如foam())。
6。2。1 初始化基础类
由于这儿涉及到两个类——基础类及衍生类,而不再是以前的一个,所以在想象衍生类的结果对象时,可能
会产生一些迷惑。从外部看,似乎新类拥有与基础类相同的接口,而且可包含一些额外的方法和字段。但继
承并非仅仅简单地复制基础类的接口了事。创建衍生类的一个对象时,它在其中包含了基础类的一个“子对
象”。这个子对象就象我们根据基础类本身创建了它的一个对象。从外部看,基础类的子对象已封装到衍生
类的对象里了。
当然,基础类子对象应该正确地初始化,而且只有一种方法能保证这一点:在构建器中执行初始化,通过调
用基础类构建器,后者有足够的能力和权限来执行对基础类的初始化。在衍生类的构建器中,Java 会自动插
入对基础类构建器的调用。下面这个例子向大家展示了对这种三级继承的应用:
//: Cartoon。java
// Constructor calls during inheritance
class Art {
Art() {