Java繼承之再談構造器
阿新 • • 發佈:2019-12-15
目錄
- 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核心技術卷