首頁  >  文章  >  Java  >  Java基類構造器呼叫的講解(附程式碼)

Java基類構造器呼叫的講解(附程式碼)

不言
不言轉載
2018-10-08 15:03:412509瀏覽

這篇文章帶給大家的內容是關於Java基類構造器呼叫的講解(附程式碼),有一定的參考價值,有需要的朋友可以參考一下,希望對你有幫助。

在《Java程式設計思想》第7章復用類別中有這樣一段話,值得深思。當子類別繼承了父類別時,就涉及了基底類別和導出類別(子類別)這兩個類別。從外部來看,導出類別就像是與基底類別有相同介面的新類別,或許還會有一些額外的方法和領域。但繼承不只是複製基底類別的介面。當創建一個導出類別對象時,該對象包含了一個基類的子對象,這個子對象與你用基類直接創建的對像是一樣的,二者區別在於,後者來自於外部,而基類的子物件是被包裹在導出類別物件內部。

這就引發出了一個很重要的問題,對基底類別子物件的正確初始化也是至關重要的(我們可能在子類別的使用基底類別中繼承的方法和領域),而且也只有一種方法來保證這一點:在子類別建構器中呼叫基底類別建構器來執行初始化。

無參的基底類別建構器

我們知道,當一個類別你沒有給他建構函數,Java會自動幫你呼叫無參的建構器,同時Java也會在匯出類別的構造器中插入對基類構造器的呼叫。下面的程式碼說明了這個工作機制:

//: reusing/Cartoon.java
// Constructor calls during inheritance.
import static net.mindview.util.Print.*;

class Art {
  Art() { print("Art constructor"); }
}

class Drawing extends Art {
  Drawing() { print("Drawing constructor"); }
}

public class Cartoon extends Drawing {
  public Cartoon() { print("Cartoon constructor"); }
  public static void main(String[] args) {
    Cartoon x = new Cartoon();
  }
} /* Output:
Art constructor
Drawing constructor
Cartoon constructor
*///:~

觀察上述程式碼的運行結果,在創建Cartoon物件時,會先呼叫其父類Drawing的建構器,而其父類又繼承自Art類,所以又會呼叫Art類別的構造器,就像層層往上。雖然在其構造器中都沒有明確地呼叫其父類別構造器,但是Java會自動呼叫其父類別的構造器。即使不為Cartoon()創建構造器,編譯器也會合成一個預設的無參構造器,該構造器將呼叫基底類別的建構器。

帶參數的基類建構器

當基類中的建構器都是帶有參數時,編譯器不會自動調用,必須用關鍵字super明確地調用基底類別建構器,並且傳入適當的參數,對應的例子程式碼如下:

//: reusing/Chess.java
// Inheritance, constructors and arguments.
import static net.mindview.util.Print.*;

class Game {
  Game(int i) {
    print("Game constructor");
  }
}

class BoardGame extends Game {
  BoardGame(int i) {
    super(i);
    print("BoardGame constructor");
  }
}   

public class Chess extends BoardGame {
  Chess() {
    super(11);
    print("Chess constructor");
  }
  public static void main(String[] args) {
    Chess x = new Chess();
  }
} /* Output:
Game constructor
BoardGame constructor
Chess constructor
*///:~

從上述程式碼中可以觀察到,必須在子類別Chess建構器中顯示的使用super呼叫父類別建構器並傳入適當參數。而且,呼叫基底類別構造器必須是在子類別建構器中做的第一件事。

基底類別建構器的呼叫順序問題

在此之前,我們先來探討一下物件參考的初始化問題。在Java中,類別中域為基本型別時能夠自動被初始化為零,但是物件參考會被初始化為null。我們往往需要在適當的位置對其進行初始化,下面是幾個可以進行初始化的位置:

1.在定義物件的地方。 這表示它們總是能夠在建構器被呼叫之前被初始化

2.在類別的建構器中。

3.就在正要使用這些物件之前,這種方式稱為惰性初始化。

記住上面的第1點,下面看一個比較複雜的例子來看基類構造器的呼叫順序問題。

// reusing/Ex7/C7.java
// TIJ4 Chapter Reusing, Exercise 7, page 246
/* Modify Exercise 5 so that A and B have constructors with arguments instead
* of default constructors. Write a constructor for C and perform all 
* initialization within C's constructor. 
*/
 
import static org.greggordon.tools.Print.*;

class A { 
    A(char c, int i) { println("A(char, int)");} 
}

class B extends A {     
    B(String s, float f){
        super(' ', 0); 
        println("B(String, float)");
    } 
}

class C7 extends A { 
    private char c;
    private int i;
    C7(char a, int j) {     
        super(a, j); 
        c = a;
        i = j;
    }
    B b = new B("hi", 1f); // will then construct another A and then a B
    public static void main(String[] args) {
        C7 c = new C7('b', 2); // will construct an A first
    }
}

上述這段程式碼輸出:

A(char, int)

#A(char, int)

B(String, float)

注意基底類別建構器、子類別建構器、類別的成員物件初始化的順序:

1.在new一個類別的物件時,首先調用其父類別建構器(可以是無參的和有參的,無參的系統會自動調用,有參的需要自己指定)。如上述C7中的super(a, j)

2.然後執行其成員物件初始化語句,呼叫B類別建構器,如上述的
B b = new B("hi", 1f),而B的建構器又會先呼叫基底類別A的建構器。

3.最後返回C7中的構造器,繼續執行c=a,i=j。

以上是Java基類構造器呼叫的講解(附程式碼)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:cnblogs.com。如有侵權,請聯絡admin@php.cn刪除