继承是所有 OOP 语言和 Java 语言的组成部分。当你在创建一个类时,你总是在继承,因
此,除非你已明确指出要从其他类中继承,否则你就是在隐式地从 Java 的标准根源类 object
进行继承。
组合的语法比较平实,但要执行继承,其语法却要采用截然不同的形式。在继承过程中,你
需要先声明:“新类与旧类相似。”通常,你首先给类确定一个名称,但在书写类主体的左边
花括号之前,应先写下关键字 extends,并在其后写下基类的名称。当你这么做时,会自动
得到基类中所有的数据成员和成员方法。例如:
//: c06:Detergent.java
// Inheritance syntax & properties.
import com.bruceeckel.simpletest.*;
class Cleanser {
protected static Test monitor = new Test();
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 String toString() { return s; }
public static void main(String[] args) {
Cleanser x = new Cleanser();
x.dilute(); x.apply(); x.scrub();
System.out.println(x);
monitor.expect(new String[] {
"Cleanser dilute() apply() scrub()"
});
}
}
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();
System.out.println(x);
System.out.println("Testing base class:");
monitor.expect(new String[] {
"Cleanser dilute() apply() " +
"Detergent.scrub() scrub() foam()",
"Testing base class:",
});
Cleanser.main(args);
}
} ///:~
这个程序示范了 Java 的许多特性。首先,在 Cleanser 的 append( )方法中,我们用“+=” 操
作符将几个 String 对象连接成 s,此操作符是 Java 设计者重载的用来处理 String 对象的两
个操作符之一(另一个是“+”)。
其次,Cleanser 和 Detergent 均含有 main( )方法。你可以为你的每个类都创建一个 main( )
方法。一般我们都建议以这种方式来编写程序代码,以使测试代码被包装在类中。即使是一
个程序中含有多个类,也只有命令行所调用的那个类的 main( )方法会被调用(只要这个
main( )是 public,其所属的类是否为 public 则不用考虑)。因此,在此例中,如果你的命令
行是 java Detergent,那么 Detergent.main( )将会被调用。但即使 Cleanser 不是一个 public 类,
如果你的命令行是 java Cleanser,那么 Cleanser.main( ) 仍然会被调用。这种在每个类中都设
置一个 main( )方法的技术可使每个类的单元测试都变得简便易行。而且你在完成单元测试
之后,也无需删除 main( ),你可以将其留待下次测试。
在此例中,你可以看到 Detergent.main ( )明确调用了 Cleanser.main( ),并将其从命令行获取
的参数传递给了它。当然,你也可以向其传递任意的 String 数组。Cleanser 中所有的方法
都必须是 public 的,这一点非常重要。请记住,如果你没有加任何访问权限修饰符,那么成
员缺省的访问权限是 package,它仅允许包内的成员访问。因此,在此包中,如果没有访问
权限修饰符,任何人都可以使用这些方法。例如,Detergent 就不成问题。但是,其他包中
的某个类若要从 Cleanser 中继承,则只能访问 public 成员。所以,为了继承,一般的规则是
将所有的数据成员都指定为 private,将所有的方法指定为 public(稍后将会学到,protected
成员也可以借助导出类来访问)。当然,在特殊情况下,你必须做出调整,但上述方法的确
是一个很有用的规则。
请注意,在 Cleanser 接口中有一组方法:append()、dilute()、apply()、scrub()和 toString()。
由于 Detergent 是由关键字 extends 从 Cleanser 导出的,所以它可以在其接口中自动获得这
些方法,尽管你并不能地看到这些方法在 Detergent 中的显式定义。因此,你可以将继承视
作是对类的复用。
正如我们在 scrub()中所见,对在基类中定义的方法进行修改是可行的。在此例中,你可能
想要在新版本中调用从基类继承而来的方法。但是在 scrub()中,你并不能直接调用 scrub(),
因为这样做将会产生递归,而这并不是你所期望的。为解决此问题,Java 提供了关键字 super。
关键字 super 指代的是当前类所继承的基类。为此,表达式 super.scrub()将调用基类版本的
scrub()方法。
在继承的过程中,你并不一定非得使用基类的方法。你也可以在导出类中添加新方法,其添
加方式与在类中添加任意方法一样,即对其加以定义即可。foam()方法即为一例。
在 Detergent.main( )中,你会发现,对于一个 Detegent 对象而言,你除了可以调用 Detergent
此,除非你已明确指出要从其他类中继承,否则你就是在隐式地从 Java 的标准根源类 object
进行继承。
组合的语法比较平实,但要执行继承,其语法却要采用截然不同的形式。在继承过程中,你
需要先声明:“新类与旧类相似。”通常,你首先给类确定一个名称,但在书写类主体的左边
花括号之前,应先写下关键字 extends,并在其后写下基类的名称。当你这么做时,会自动
得到基类中所有的数据成员和成员方法。例如:
//: c06:Detergent.java
// Inheritance syntax & properties.
import com.bruceeckel.simpletest.*;
class Cleanser {
protected static Test monitor = new Test();
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 String toString() { return s; }
public static void main(String[] args) {
Cleanser x = new Cleanser();
x.dilute(); x.apply(); x.scrub();
System.out.println(x);
monitor.expect(new String[] {
"Cleanser dilute() apply() scrub()"
});
}
}
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();
System.out.println(x);
System.out.println("Testing base class:");
monitor.expect(new String[] {
"Cleanser dilute() apply() " +
"Detergent.scrub() scrub() foam()",
"Testing base class:",
});
Cleanser.main(args);
}
} ///:~
这个程序示范了 Java 的许多特性。首先,在 Cleanser 的 append( )方法中,我们用“+=” 操
作符将几个 String 对象连接成 s,此操作符是 Java 设计者重载的用来处理 String 对象的两
个操作符之一(另一个是“+”)。
其次,Cleanser 和 Detergent 均含有 main( )方法。你可以为你的每个类都创建一个 main( )
方法。一般我们都建议以这种方式来编写程序代码,以使测试代码被包装在类中。即使是一
个程序中含有多个类,也只有命令行所调用的那个类的 main( )方法会被调用(只要这个
main( )是 public,其所属的类是否为 public 则不用考虑)。因此,在此例中,如果你的命令
行是 java Detergent,那么 Detergent.main( )将会被调用。但即使 Cleanser 不是一个 public 类,
如果你的命令行是 java Cleanser,那么 Cleanser.main( ) 仍然会被调用。这种在每个类中都设
置一个 main( )方法的技术可使每个类的单元测试都变得简便易行。而且你在完成单元测试
之后,也无需删除 main( ),你可以将其留待下次测试。
在此例中,你可以看到 Detergent.main ( )明确调用了 Cleanser.main( ),并将其从命令行获取
的参数传递给了它。当然,你也可以向其传递任意的 String 数组。Cleanser 中所有的方法
都必须是 public 的,这一点非常重要。请记住,如果你没有加任何访问权限修饰符,那么成
员缺省的访问权限是 package,它仅允许包内的成员访问。因此,在此包中,如果没有访问
权限修饰符,任何人都可以使用这些方法。例如,Detergent 就不成问题。但是,其他包中
的某个类若要从 Cleanser 中继承,则只能访问 public 成员。所以,为了继承,一般的规则是
将所有的数据成员都指定为 private,将所有的方法指定为 public(稍后将会学到,protected
成员也可以借助导出类来访问)。当然,在特殊情况下,你必须做出调整,但上述方法的确
是一个很有用的规则。
请注意,在 Cleanser 接口中有一组方法:append()、dilute()、apply()、scrub()和 toString()。
由于 Detergent 是由关键字 extends 从 Cleanser 导出的,所以它可以在其接口中自动获得这
些方法,尽管你并不能地看到这些方法在 Detergent 中的显式定义。因此,你可以将继承视
作是对类的复用。
正如我们在 scrub()中所见,对在基类中定义的方法进行修改是可行的。在此例中,你可能
想要在新版本中调用从基类继承而来的方法。但是在 scrub()中,你并不能直接调用 scrub(),
因为这样做将会产生递归,而这并不是你所期望的。为解决此问题,Java 提供了关键字 super。
关键字 super 指代的是当前类所继承的基类。为此,表达式 super.scrub()将调用基类版本的
scrub()方法。
在继承的过程中,你并不一定非得使用基类的方法。你也可以在导出类中添加新方法,其添
加方式与在类中添加任意方法一样,即对其加以定义即可。foam()方法即为一例。
在 Detergent.main( )中,你会发现,对于一个 Detegent 对象而言,你除了可以调用 Detergent
的方法(如 foam())之外,还可以调用 Cleanser 中所有可用的方法。