1. 程式人生 > >Java繼承之再談構造器

Java繼承之再談構造器

目錄

  • Java繼承之再談構造器
    • 初始化基類
    • 預設構造器
    • 帶引數的構造器
    • 子類呼叫父類構造器

Java繼承之再談構造器

初始化基類

前面提到,繼承是子類對父類的拓展。《Thinking in Java》中提到下面一段話:

當建立一個匯出類的物件時,該物件包含了一個基類的子物件。這個子物件與你用基類直接建立的物件是一樣的。二者區別在於,後者來自於外部,而基類的子物件被包裝在匯出類的物件內部。

我們在建立子類物件時,呼叫了父類的構造器,甚至父類的父類構造器。我們知道,構造器用於建立物件,那麼突然產生疑惑:關於建立一個子類物件時,是否會先建立父類物件?


經過查詢資料,得出結論:
並沒有。在建立子類物件時,會把父類的成員變數和方法載入進記憶體,既然要載入,便呼叫父類構造器看看這些資料是如何進行初始化的,僅此而已,並不是建立了父類的物件。
所以,可以看作,子類物件中包含著父類的子物件。我們知道,物件的初始化是至關重要的。那麼,這個父類的子物件如何正確初始化呢?對了,就是接下來要說的:在構造器中呼叫基類構造器來執行初始化。
注意:子類並不能繼承父類的構造器,只是單純呼叫了基類構造器中的初始化程式碼。

預設構造器

先看一段簡單的測試程式碼:

package com.my.pac13;
/*繼承中的構造*/
public class Person {
    Person(){
        System.out.println("Person()");
    }
}
class Student extends Person{
    Student(){
        System.out.println("Student()");
    }
}
class PrimaryStudent extends Student{
    PrimaryStudent(){
        //super();
        System.out.println("PrimaryStudent()");
    }
    public static void main(String[] args) {
        //建立了PrimaryStudent物件
        new PrimaryStudent();
    }
}
/*
 Person()
 Student()
 PrimaryStudent()
*/

關於構造器,我們前面提到,任何沒有顯式構造器的類都存在著一個無引數的預設構造器。我們上面的例子在預設構造器中加入了列印輸出,以便理解。
可以看到的是:

  • 在建立PrimaryStudent時,他的直接父類Student和間接父類Person中的構造器都被呼叫了,而且可以看到,是"自上而下"的。
  • 父類在子類構造器可以訪問它之前,就已經完成了初始化的操作。
  • 若子類沒有顯式呼叫父類的構造器,則自動呼叫父類的預設(無參)構造器。

帶引數的構造器

前面的程式碼中,每個類都含有預設的構造器,建立子類物件時,是自上而下,且子類會預設呼叫父類的無參構造器。那麼,假設父類正好沒有無參構造器或者你正想呼叫父類的帶參構造器,這時就需要我們的super關鍵字。(super關鍵字之後還會進行總結)

我們直接在原來的基礎上稍作修改,並進行測試。

package com.my.pac13;
/*呼叫基類構造器是子類構造器中要做的第一件事*/
public class Person {
    //沒有預設構造器
    Person(String name){
        System.out.println("Person()\t"+name);
    }
}
class Student extends Person{
    //也沒有預設構造器,且用super顯式呼叫
    Student(String n){
    //super關鍵字呼叫父類的構造器
        super(n);
        System.out.println("一引數Student\t"+n);
    }
    Student(String n,String m){
    //this關鍵字呼叫同一類中過載的構造器
        this(n);
        System.out.println("二引數student()\t"+m);
    }
}
class PrimaryStudent extends Student{
    //隱式呼叫父類構無引數構造器,但是父類沒有,所以要用super顯式呼叫
    PrimaryStudent(){
    //沒有下面的語句會報錯
        super("hello");
        System.out.println("PrimaryStudent()");
    }

}
class ExtendsTest{
    public static void main(String[] args) {
        new Person("the shy");
        System.out.println("***********");
        new Student("rookie");
        System.out.println("***********");
        new Student("the shy","rookie");
        System.out.println("***********");
        new PrimaryStudent();
        System.out.println("***********");
    }

}
/*
Person()    the shy
***********
Person()    rookie
一引數Student  rookie
***********
Person()    the shy
一引數Student  the shy
二引數student()    rookie
***********
Person()    hello
一引數Student  hello
PrimaryStudent()
***********
 */
  • this是正在建立的物件,用於呼叫同一類中過載的構造器,可以參看我之前的文章:Java關鍵字之this。
  • super在呼叫構造器時,使用方法和this相似。(但super和this本身有本質的不同,super並不是一個物件的引用!!!)
  • super和this語句都必須出現在第一行,也就是說一個構造器中只能有其中之一。

子類呼叫父類構造器

無論是否使用super語句來呼叫父類構造器的初始化程式碼,子類構造器總是會事先呼叫父類構造器!這是一定要記住的!

  • 子類構造器A在第一行顯式使用super呼叫父類構造器B,格式super(引數列表),根據引數列表選擇對應的父類構造器。
//父類
 Person(String name){
        System.out.println("Person()\t"+name);
    }
//子類
 Student(String n){
    //super關鍵字呼叫父類的構造器
        super(n);
        System.out.println("一引數Student\t"+n);
    }
  • 子類構造器A先用this呼叫本類過載的構造器B,然後B呼叫父類構造器。
//父類
 Person(String name){
        System.out.println("Person()\t"+name);
    }
//子類
Student(String n){
    //super關鍵字呼叫父類的構造器
        super(n);
        System.out.println("一引數Student\t"+n);
    }
Student(String n,String m){
//this關鍵字呼叫同一類中過載的構造器
    this(n);
    System.out.println("二引數student()\t"+m);
}
  • 子類構造器中沒有super和this時,系統會隱式呼叫父類的無參構造器,要是沒有無參的,那就報錯。
//隱式呼叫父類構無引數構造器,但是父類沒有,所以要用super顯式呼叫
PrimaryStudent(){
//沒有下面的語句會報錯
    super("hello");
    System.out.println("PrimaryStudent()");
}

綜上所述:

當呼叫子類構造器對子類物件進行初始化時,父類構造器總會在子類構造器之前執行。甚至,父類的父類會在父類之前執行……一直追溯到所有類的超類Object類的構造器。

參考書籍:《Thinking in Java》、《瘋狂Java講義》、《Java核心技術卷