Java建構函式的繼承

惹不起的程咬金發表於2011-08-21

 

專家讀書筆記,希望能夠對大家學習java有所幫助

"每個子類構造方法的第一條語句,都是隱含地呼叫super(),如果父類沒有這種形式的建構函式,那麼在編譯的時候就會報錯。"

這句話怎麼理解

所有程式碼都經過測試,測試環境:

java   version   "1.4.0-rc "

Java(TM)   2   Runtime   Environment,   Standard   Edition   (build   1.4.0-rc-b91)

Java   HotSpot(TM)   Client   VM   (build   1.4.0-rc-b91,   mixed   mode)

如大家發現任何錯誤,或有任何意見請不吝賜教。

預設建構函式的問題:base類是父類,derived類是子類,首先要

說明的是由於先有父類後有子類,所以生成子類之前要首先有父類。

class是由class的建構函式constructor產生的,每一個class都有

建構函式,如果你在編寫自己的class時沒有編寫任何建構函式,那麼

編譯器為你自動產生一個預設default建構函式。這個default建構函式

實質是空的,其中不包含任何程式碼。但是一牽扯到繼承,它的問題就出現

了。

如果父類base   class只有預設建構函式,也就是編譯器自動為你產生的。

而子類中也只有預設建構函式,那麼不會產生任何問題,因為當你試圖產生

一個子類的例項時,首先要執行子類的建構函式,但是由於子類繼承父類,

所以子類的預設建構函式自動呼叫父類的預設建構函式。先產生父類的例項,

然後再產生子類的例項。如下:

class   base{

}

class   derived   extends   base{

public   static   void   main(String[]   args){

derived   d=new   derived();

}

}

下面我自己顯式地加上了預設建構函式:

class   base{

base(){

System.out.println( "base   constructor ");

}

}

class   derived   extends   base{

derived(){

System.out.println( "derived   constructor ");

}

public   static   void   main(String[]   args){

derived   d=new   derived();

}

}

執行結果如下:說明了先產生base   class然後是derived   class

base   constructor

derived   constructor

我要說明的問題出在如果base   class有多個constructor

derived   class也有多個constructor,這時子類中的建構函式預設

呼叫那個父類的建構函式呢?答案是呼叫父類的預設建構函式。

但是不是編譯器自動為你生成的那個預設建構函式而是你自己顯式地

寫出來的預設建構函式。

class   base{

base(){

System.out.println( "base   constructor ");

}

base(int   i){

System.out.println( "base   constructor   int   i ");

}

}

class   derived   extends   base{

derived(){

System.out.println( "derived   constructor ");

}

derived(int   i){

System.out.println( "derived   constructor   int   i ");

}

public   static   void   main(String[]   args){

derived   d=new   derived();

derived   t=new   derived(9);

}

}

D:"java"thinking"think6> java   derived

base   constructor

derived   constructor

base   constructor

derived   constructor   int   i

如果將base  類的建構函式註釋掉,則出錯。

class   base{

//   base(){

//   System.out.println( "base   constructor ");

//   }

base(int   i){

System.out.println( "base   constructor   int   i ");

}

}

class   derived   extends   base{

derived(){

System.out.println( "derived   constructor ");

}

derived(int   i){

System.out.println( "derived   constructor   int   i ");

}

public   static   void   main(String[]   args){

derived   d=new   derived();

derived   t=new   derived(9);

}

}

 

D:"java"thinking"think6> javac   derived.java

derived.java:10:   cannot   resolve   symbol

symbol   :   constructor   base   ()

location:   class   base

derived(){

^

derived.java:13:   cannot   resolve   symbol

symbol   :   constructor   base   ()

location:   class   base

derived(int   i){

2   errors

說明子類中的建構函式找不到顯式寫出的父類中的預設

建構函式,所以出錯。

那麼如果你不想子類的建構函式呼叫你顯式寫出的父類中的預設

建構函式怎麼辦呢?

如下例:

class   base{

//   base(){

//   System.out.println( "base   constructor ");

//   }

base(int   i){

System.out.println( "base   constructor   int   i ");

}

}

class   derived   extends   base{

derived(){

super(8);

System.out.println( "derived   constructor ");

}

derived(int   i){

super(i);

System.out.println( "derived   constructor   int   i ");

}

public   static   void   main(String[]   args){

derived   d=new   derived();

derived   t=new   derived(9);

}

}

 

D:"java"thinking"think6> java   derived

base   constructor   int   i

derived   constructor

base   constructor   int   i

derived   constructor   int   i

super(i)表示父類的建構函式base(i)請大家注意

一個是super(i)一個是super(8)

大家想想是為什麼??

結論:子類如果有多個建構函式的時候,父類要麼沒有建構函式,

讓編譯器自動產生,那麼在執行子類建構函式之前先執行編

譯器自動產生的父類的預設建構函式;要麼至少要有一個顯

式的預設建構函式可以讓子類的建構函式呼叫。


java建構函式的呼叫順序:

當一個複雜的物件被構造時,它的建構函式按下面的順序被呼叫(that the order of constructor calls for a complex object is as follows)

   1.其基類(base-class)的建構函式被呼叫,這個步驟以遞迴的方式重複,所以最底層(the root ofhierarchy)的建構函式首先被執行,然後是它上一層派生類(the next-derived class)...直到最頂層的派生類(themost-derived class).

The base-class constructor is called. Thisstep is repeated recursively such that the root of the hierarchy isconstructed first, followed by the next-derived class, etc., until themost-derived class is reached.)

    2.如果有包含關係(composition),那麼它的成員物件按照宣告的順序被構造.

Member initializers are called in the order of declaration.

    3.派生類建構函式的內容(body)被執行.

The body of the derived-class constructor is called.

    一個例項:


class Cake{
   Cake(){System.out.println(
"Cake()");}
}

class Meal {
  Meal() { System.out.println(
"Meal()"); }
}

class Bread {
  Bread() { System.out.println(
"Bread()"); }
}

class Cheese {
  Cheese() { System.out.println(
"Cheese()"); }
}

class Lettuce {
  Lettuce() { System.out.println(
"Lettuce()"); }
}

class Lunch extends Meal {
  Lunch() { System.out.println(
"Lunch()"); }
}

class PortableLunch extends Lunch {
   
//if make derived-class object as the menber of the base-class will lead a infinite
   
//loop and program will stop because of the memory consumed
   
   
//private Sandwich s=new Sandwich(); 
  private Cake a=new Cake();
  PortableLunch() { System.out.println(
"PortableLunch()");}
}

public class Sandwich extends PortableLunch
{
  
private Bread b = new Bread();
  
private Cheese c = new Cheese();
  
private Lettuce l = new Lettuce();
  
  
public Sandwich() {
    System.out.println(
"Sandwich()");
  }
  
  
public static void main(String[] args)  {
    
new Sandwich();
  }
}

輸出:

Meal()
Lunch()
Cake()
PortableLunch()
Bread()
Cheese()
Lettuce()
Sandwich()

  main()函式中要構造一個Sandwich的物件,呼叫(並不是執行)它基類PortableLunch的建構函式,PortableLunch又遞迴的呼叫,然後是Meal,Meal是繼承的最底層的基類(不算Object)所以它的建構函式首先被執行,然後按次序返回到Lunch,PortableLunch,但在PortableLunch的建構函式被執行之前,它的成員物件Cakea先按照宣告的順序被構造.然後執行PortableLunch(),接著是Sandwich的成員物件,最後是Sandwich().

注:被註釋掉的程式碼,將base-class的物件作為derive-class的成員物件,這樣會遞迴無法結束,最後程式因堆疊耗盡而結束(Exception in  thread main java.lang.StackOverflowError).







相關文章