调用超类的构造器
? ? ? ?子类和普通的类一样,可以利用关键字new来创建它的实例。如果没有在子类中显示编写一个构造器,编译器就会隐式添加一个无参(no-arg)构造器
? ? ? ?当通过调用子类的某个构造器来将它实例化时,构造器的第一个动作就是调用直接父类的无参构造器。在父类中,构造器也调用其直接父类的构造器。这个过程会不断重复,直到到达java.lang.Object类的构造器为止。换句话说,当创建一个子类时,他所有的父类也就实例化了。
? ? ? ?下面以Base和Sub类为例演示了这个过程。
class="java">class Base { public Base() { System.out.println("Base"); } public Base(String s) { System.out.println("Base." + s); } } public class Sub extends Base { public Sub(String s) { System.out.println(s); } public static void main(String[] args) { Sub sub = new Sub("Start"); } }
? ? ? ?如果运行Sub类,就会在控制台看到以下结果:
? ? ? ?Base
? ? ? ?Start
? ? ? ?这表明,Sub类的构造器的第一个动作就是调用Base类的无参构造器。Java编译器已经悄悄地将Sub的构造器改成下面的样子,但是,这一修改并没有真正保存到源文件中。
public Sub(String s) { super(); System.out.println(s); }
?? ? ? ? 关键字super表示当前对象的直接超类的一个实例。由于super是从Sub的一个实例中调用的,因此super表示其直接超类Base的一个实例。
? ? ? ?通过使用关键字super,可以从子类的构造器显示调用父类的构造器,但super必须是构造器的第一条语句,如果你想调用超类中的另一个构造器的话,使用关键字super是很方便的。例如,可以将Sub中的构造器修改成下面这样:
public Sub(String s) { super(s); System.out.println(s); }
? ? ? ?这个构造器利用super(s),调用父类的单参数构造器。结果,当运行这个类时,就会在控制台中看到以下内容:
? ? ? ?Base.Start
? ? ? ?Start
? ? ? ?那么,如果超类没有无参构造器,并且也没有从子类显示调用另一个构造器,会怎么样呢?以下代码以Parent类和Child类为例做了展示。
class Parent { public Parent (String s) { System.out.println("Parent (String)"); } } public class Child extends Parent { public Child() { } }
? ? ? ?这样将会产生一个编译错误,因为编译器添加了一个对Parent中无参构造器的隐式调用,而Parent类只有一个带有String的构造器。只要从Child类的构造器中显示调用父类的构造器,就可以纠正这种问题:
public Child() { super(null); }
? ? ? ?提示:对于一个子类而言,能够从它自己的构造器调用其父类的构造器,这是很有意义的事情,因为子类的 实例必须始终配有其每个父类的实例。因此,对子类中未覆盖的方法的调用,将被传递给其父类,直到在该继承层次结构中找到第一个方法为止。
?
调用超类的隐藏成员
? ? ? ?关键字super的存在还有另一个使命。它可以用来调用超类中隐藏的成员,或者覆盖的方法。由于super表示直接父类的一个实例,因此,super.memberName将返回父类中的指定成员。可以访问到超类中能通过子类看到的任何成员。例如,以下代码展示了两个具有父子关系的类:Tool和Pencil。
Class Tool { public String toString() { return "Generic tool"; } } public class Pencil extends Tools { public String toString() { return "I am a Pencil"; } public void write() { System.out.println(super.toString); System.out.println(toString()); } public static void main(String[] args) { Pencil pencil = new Pencil(); Pencil.write; } }
? ? ? ?Pencil类覆盖Tool中的toString方法。如果运行Pencil类,就会在控制台中看到以下结果:
? ? ? ?Generic tool
? ? ? ?I am a pencil
? ? ? ?与调用父类的构造器不同,调用父类的成员的语句不需要放在调用方法中的第一行。