java的繼承機制

gudesheng發表於2008-01-03

這次我想深入探究以下java裡類的繼承機制。

我們知道,繼承是java設計裡的一個失敗的地方。高司令說過:如果讓他重新設計java的話,他會把繼承去掉。而java裡繼承到底怎麼了,會這麼不受人歡迎呢?

我們知道,繼承設計的初衷是為了程式碼複用。在C++裡也確實做到了這一點,因為C++允許多重繼承。特別是C++裡的解構函式,申明為了virtual的時候可以多重複用,用起來也很舒服。

那麼為什麼在java裡繼承就這麼讓人詬病呢?

第一:java裡的類不能多重繼承,如果一個類一旦繼承了它的父類,那麼它就別想再繼承別的類。一個兒子只能有一個老爸,原本設計是沒錯的。可是在實際應用中,就會出現各種問題。

第二:java裡有更好的介面,介面從本質上來說可以更好的代替繼承。實現像C++裡一樣的巨集定義的功能,把你想要的東西都抽象在介面裡,讓具體的類去實現這些功能。

如果你去面試過,肯定遇到過不少考察java繼承機制的題目,其中最顯著的一個就是建構函式的呼叫和重寫方法的呼叫。

這裡我再強調一下過載和重寫:

過載是同一個類裡面相同方法名,不同引數型別或個數的方法。這也是C++類裡面為什麼出現函式模板的原因,就拿求和來說:

int add(int a,int b);

long add(long a,long b);

int main(){

}

因為求和可能是求int的和,也可能是求long的和,這個時候就出現了需求(我始終認為,一種東西的出現必定是因為對他的需求)

而重寫呢,就是子類對父類裡的方法的重改,就是他改寫了父類的方法。倫理上看似不孝,而道理上是事物總是要不斷髮展的。這就像是社會的改革一樣。

下面我來分析一下父類和子類的建構函式呼叫順序:

在記憶體機制中,父類和子類是佔用同一塊記憶體的,只不過子類在父類的基礎上增加了自己的部分(包括資料成員和屬性),這樣一來就好理解了。子類是依附於父類的,先有父類再有子類。所以說一個子類物件的產生,必須先呼叫父類的建構函式產生一個父類例項,然後在這個例項基礎上新增自己的部分。

而實際的執行機制,也正是這樣的。

因為這樣就很容易理解了,先呼叫父類的建構函式,再呼叫子類的建構函式。

而對於父類和子類裡重寫的方法的呼叫,關鍵要看:子類到底是否產生,如果子類產生了,子類改寫了父類的方法,看似父類和子類各自有一個方法,其實它們在記憶體模型裡佔用的是同一塊記憶體,子類方法會覆蓋父類方法。

我們看下面的程式:

class SuperStringTest {

    SuperStringTest(){

           System.out.println("Father is constructed.");

    }

   

    public void test(){

           System.out.println("Father is running.");

    }

}

 

public class StringTest extends SuperStringTest{

      

       StringTest(){

           System.out.println("Son is constructed.");

    }

      

       public void test(){

           System.out.println("Son is running.");

    }

      

       public static void main(String[] args){

              /*

              SuperStringTest sst = new StringTest();

 

              此時,派生類的方法覆蓋了基類的方法,基類的方法對派生類來說為不可見(有先像作用域),也就是派生類裡的同名方法重新寫了基類的同名方法。此時,對基類和派生類來說只有被派生類改寫後的唯一的一個方法。所以,只能呼叫派生類的方法。

 

              Father is constructed.

              Son is constructed.

              Son is running.

              */

             

              /*

              StringTest sst = new StringTest();

             

解釋同上面

 

              Father is constructed.

              Son is constructed.

              Son is running.

              */

             

              /*

              SuperStringTest sst = new SuperStringTest();

             

        此時,基類所佔的記憶體單元中並沒有派生類的東西。所以,方法沒被改寫,呼叫父類的方法。

 

              Father is constructed.

              Father is running.

        */

              sst.test();

       }

}

 



Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=640253


相關文章