- 写在前面的话
- 引言
- 第 1 章 对象入门
- 第 2 章 一切都是对象
- 第 3 章 控制程序流程
- 第 4 章 初始化和清除
- 第 5 章 隐藏实施过程
- 第 6 章 类再生
- 第 7 章 多形性
- 第 8 章 对象的容纳
- 第 9 章 违例差错控制
- 第 10 章 Java IO 系统
- 第 11 章 运行期类型鉴定
- 第 12 章 传递和返回对象
- 第 十三 章 创建窗口和程序片
- 第 14 章 多线程
- 第 15 章 网络编程
- 第 16 章 设计范式
- 第 17 章 项目
- 附录 A 使用非 JAVA 代码
- 附录 B 对比 C++和 Java
- 附录 C Java 编程规则
- 附录 D 性能
- 附录 E 关于垃圾收集的一些话
- 附录 F 推荐读物
6.2 继承的语法
继承与 Java(以及其他 OOP 语言)非常紧密地结合在一起。我们早在第 1 章就为大家引入了继承的概念,并在那章之后到本章之前的各章里不时用到,因为一些特殊的场合要求必须使用继承。除此以外,创建一个类时肯定会进行继承,因为若非如此,会从 Java 的标准根类 Object 中继承。
用于合成的语法是非常简单且直观的。但为了进行继承,必须采用一种全然不同的形式。需要继承的时候,我们会说:“这个新类和那个旧类差不多。”为了在代码里表面这一观念,需要给出类名。但在类主体的起始花括号之前,需要放置一个关键字 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() 的调用是明确进行的。
需要着重强调的是 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())。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论