JAVA類繼承過程中其成員的一些問題
JAVA類繼承過程中其成員的一些問題
???
一、構造函數的問題:
???構造函數不能繼承。子類的構造函數可以通過super關鍵字顯式調用父類中的構造函數。如果子類中的構造函數沒有顯式調用父類中的構造函數,編譯器就會自動在子類的構造函數中調用父類中參數為空的構造函數。于是,當父類中沒有參數為空的構造函數,而子類中又沒有顯示調用父類的其他構造函數,編譯時就會報錯。這一點需要特別注意。當父類中沒有定義任何構造函數時,編譯器就會為它指定一個參數為空的默認的構造函數;如果父類中定義了構造函數,那么編譯器就不會為它指定一個參數為空的默認構造函數了。因此,如果某個類有可能成為其他類的父類,為了避免發生不必要的編譯錯誤,最好為它編寫一個參數為空的構造函數。
eg1.父類Sup中沒有定義構造函數,編譯程序將為它指定一個參數為空的默認構造函數。子類Sub中也沒有定義構造函數,編譯程序也會為它指定一個參數為空的默認的構造函數,并且會在這個默認的構造函數中調用父類的參數為空的構造函數。

public?class?Sub??extends?Sup
{
????//子類中沒有定義構造函數

????public?static?void?main(String?args[])
{
????????Sub?sub=new?Sub();
????}
}

class?Sup
{
????//父類中沒有定義構造函數
}
public?class?Sub??extends?Sup
{
????//子類中定義類一個帶整型變量參數的構造函數

????public?Sub(int?i)
{
????????//
????}

????public?static?void?main(String?args[])
{
????????Sub?sub=new?Sub(1);
????}
}

class?Sup
{
????//父類中沒有定義構造函數
}
public?class?Sub??extends?Sup
{
????//子類中定義類一個帶整型變量參數的構造函數

????public?Sub(int?i)
{
????????//
????}

????public?static?void?main(String?args[])
{
????????Sub?sub=new?Sub(1);
????}
}


class?Sup
{
????//父類中定義了一個帶整型參數的構造函數

????public?Sup(int?i)
{
????????//
????}
}
public?class?Sub??extends?Sup
{
????//子類中定義類一個帶整型變量參數的構造函數

????public?Sub(int?i)
{
????????super(i);//調用父類中的構造函數
????}

????public?static?void?main(String?args[])
{
????????Sub?sub=new?Sub(1);
????}
}


class?Sup
{
????//父類中定義了一個帶整型參數的構造函數

????public?Sup(int?i)
{
????????//
????}
}
public?class?Sub??extends?Sup
{
????//子類中定義類一個帶整型變量參數的構造函數

????public?Sub(int?i)
{
????????//
????}

????public?static?void?main(String?args[])
{
????????Sub?sub=new?Sub(1);
????}
}


class?Sup
{
????//父類中定義了一個帶整型參數的構造函數

????public?Sup(int?i)
{
????????//
????}
????//定義一個帶空參數的構造函數

????public?Sup()
{
????????//
????}
}
public?class?Sub??extends?Sup
{
????//子類中的這個方法父類中的方法有相同的標記,
????//但返回值不同,編譯程序就會報錯

????int?test()
{
????????return??1;
????}
}


class?Sup
{

????void?test()
{
????????//
????}
}
public?class?Sub??extends?Sup
{
????//子類中的方法正確覆蓋了父類中的方法

????void?test()
{
????????//
????}
}


class?Sup
{

????void?test()
{
????????//
????}
}
???同樣如果子類的類方法和父類的類方法具有相同的標記,那么子類的類方法將隱藏父類的類方法。注意這里不叫覆蓋,而叫隱藏,兩者的區別將在后面討論。同樣,如果子類的類方法和父類的類方法具有相同的標記和不同的返回值,編譯程序也會報錯。解決的問題的方法同實例方法一樣。
???但是實例方法不能覆蓋類方法,類方法也不能隱藏實例方法,否則編譯程序就會報錯。
???
???實例方法不能覆蓋類方法(編譯時報錯):

public?class?Sub??extends?Sup
{
????//實例方法不能覆蓋類方法,編譯時報錯

????void?test()
{
????????//
????}
}


class?Sup
{

????static?void?test()
{
????????//
????}
}
???類方法不能隱藏實例方法(編譯時報錯):
public?class?Sub??extends?Sup
{
????//類方法不能隱藏實例方法,編譯時報錯

????static?void?test()
{
????????//
????}
}


class?Sup
{

?????void?test()
{
????????//
????}
}
???覆蓋和隱藏的區別。對于隱藏的方法,運行時系統調用當前對象引用的編譯時類型中定義的方法;對于覆蓋的方法,運行時系統調用當前對象引用運行時類型中定義的方法。一個是調用編譯時類型中定義的方法,一個是調用運行時類型中定義的方法。通過下面這個例子可以直觀的看出兩者之間的差別。

class?Planet?
{

????public?static?void?hide()?
{
????????System.out.println("The?hide?method?in?Planet.");
????}


????public?void?override()?
{
????????System.out.println("The?override?method?in?Planet.");
????}
}


class?Earth?extends?Planet?
{

????public?static?void?hide()?
{
????????System.out.println("The?hide?method?in?Earth.");
????}


????public?void?override()?
{
????????System.out.println("The?override?method?in?Earth.");
????}


????public?static?void?main(String?args[])?
{
????????Earth?earth?=?new?Earth();
????????Planet?planet?=?(Planet)?earth;
????????planet.hide();?//?result:"The?hide?method?in?Planet."
????????planet.override();?//?result:"The?override?method?in?Earth."
????}
}
???Earth類覆蓋了Planet類的override方法,并隱藏了它的hide方法。在main方法中創建了一個Earth的實例,并將此實例轉換為一個Planet的引用。然后通過Planet引用調用override方法和hide方法。hide方法是類方法,因此根據引用的類來決定調用哪個方法,所以planet.hide()調用的是Planet的類方法hide。override方法是實例方法,因此調用的是運行時的實例中的方法,因為planet引用的是Earth的實例,因此planet.override()調用的是Earth類中實例方法override。
???JAVA正是通過實例方法的這種覆蓋關系來實現多態的目的。
三、類的變量在繼承過程中的問題:
???
???如果子類中的變量如果和父類中的變量具有相同的標記——即相同的名字——那么子類中的變量一就會隱藏父類中的變量。不過他們的類型是什么,也不管他們是類變量還是實例變量。只要他們具有相同的名字。

public?class?Sub?extends?Sup?
{
????float?i;?//子類中的實例變量隱藏父類中的類變量,并且具有不同的類型
????float?j;?//子類中的類變量隱藏父類中的實例變量,并且具有不同的類型
????int?k;?//子類中的實例變量隱藏父類中的實例變量
????static?int?l;?//子類中的類變量隱藏父類中的類變量
}


class?Sup?
{
????static?int?i?;
????int?j;
????int?k;
????static?int?l;
}
???同子類中的類方法隱藏父類中的類方法一樣,當子類中的變量覆蓋父類中的變量的時候,到底使用哪個變量,要根據引用的類型來確定。
???下面是一個類成員繼承關系的綜合例子:
package?basic;


public?class?Subclass?extends?Superclass?
{
????static?String?staticString?=?"static?String?in?Subclass";

????String?instanceString?=?"instance?String?in?Subclass";


????static?void?staticPrintf(float?a)?
{
????????System.out.println("static?printf?method?in?Subclass");
????}


????void?instancePrintf(float?a)?
{
????????System.out.println("instance?printf?method?in?Subclass");
????}
????


????public?static?void?main(String?args[])?
{
????????Subclass?sub?=?new?Subclass();
????????Superclass?sup?=?(Superclass)?sub;
????????System.out.println(sub.staticString);?//?static?String?in?Subclass
????????System.out.println(sub.instanceString);?//?instance?String?in?Subclass
????????System.out.println(sup.staticString);?//?static?String?in?Superclass
????????System.out.println(sup.instanceString);?//?instance?String?in?Superclass

????????sub.staticPrintf(1);?//?static?printf?method?in?Subclass
????????sub.instancePrintf(1);?//?instance?printf?method?in?Subclass
????????sup.staticPrintf(1);?//?static?printf?method?in?Superclass
????????sup.instancePrintf(1);?//?instance?printf?method?in?Subclass
????}
}


class?Superclass?
{
????static?String?staticString?=?"static?String?in?Superclass";

????String?instanceString?=?"instance?String?in?Superclass";


????static?void?staticPrintf(float?a)?
{
????????System.out.println("static?printf?method?in?Superclass");
????}


????void?instancePrintf(float?a)?
{
????????System.out.println("instance?printf?method?in?Subclass");
????}
????
}
???
一、構造函數的問題:
???構造函數不能繼承。子類的構造函數可以通過super關鍵字顯式調用父類中的構造函數。如果子類中的構造函數沒有顯式調用父類中的構造函數,編譯器就會自動在子類的構造函數中調用父類中參數為空的構造函數。于是,當父類中沒有參數為空的構造函數,而子類中又沒有顯示調用父類的其他構造函數,編譯時就會報錯。這一點需要特別注意。當父類中沒有定義任何構造函數時,編譯器就會為它指定一個參數為空的默認的構造函數;如果父類中定義了構造函數,那么編譯器就不會為它指定一個參數為空的默認構造函數了。因此,如果某個類有可能成為其他類的父類,為了避免發生不必要的編譯錯誤,最好為它編寫一個參數為空的構造函數。
eg1.父類Sup中沒有定義構造函數,編譯程序將為它指定一個參數為空的默認構造函數。子類Sub中也沒有定義構造函數,編譯程序也會為它指定一個參數為空的默認的構造函數,并且會在這個默認的構造函數中調用父類的參數為空的構造函數。















eg2.父類Sup中沒有定義構造函數,編譯程序將為它指定一個參數為空的默認構造函數。子類定義了一個帶整型參數的構造函數,在這個構造函數中子類沒有顯式調用父類的構造函數,所以編譯器為在它里面調用父類中參數為空的構造函數。





















eg3.父類中定義了一個帶整型參數的構造函數,因此編譯器不再為它指定參數為空的默認的構造函數。子類中也定義了一個帶整型參數的構造函數。編譯時,編譯器將試圖在子類的構造函數中調用父類的參數為空的構造函數,但是父類中沒有定義參數為空的構造函數,所以編譯程序將會報錯。排錯的方法時在子類的構造函數中顯示調用父類的構造函數,或者在父類中添加一個帶空參數的構造函數。




























???排錯方法1:



























???排錯方法2:



































二、類方法和實例方法在繼承過程中的問題:
???
???首相要明確一個概念,JAVA中通過什么來區別不同的成員——答案是標記。所謂標記,對于方法而言是指方法的名字、參數的數量和參數的類型;對于變量而言是指變量的名稱。注意,標記不包括方法的返回值和變量的類型。
???如果子類的實例方法和父類的實例方法具有相同的標記,子類的方法將覆蓋父類的方法。但是如果子類的方法和父類的方法具有相同的標記,但是具有不同的返回值類型,編譯程序將會報錯。排錯的方法是修改子類中這個方法的返回值類型,使它父類中相同標記的方法的返回值一樣。






















???排錯:






















???同樣如果子類的類方法和父類的類方法具有相同的標記,那么子類的類方法將隱藏父類的類方法。注意這里不叫覆蓋,而叫隱藏,兩者的區別將在后面討論。同樣,如果子類的類方法和父類的類方法具有相同的標記和不同的返回值,編譯程序也會報錯。解決的問題的方法同實例方法一樣。
???但是實例方法不能覆蓋類方法,類方法也不能隱藏實例方法,否則編譯程序就會報錯。
???
???實例方法不能覆蓋類方法(編譯時報錯):






















???類方法不能隱藏實例方法(編譯時報錯):






















???覆蓋和隱藏的區別。對于隱藏的方法,運行時系統調用當前對象引用的編譯時類型中定義的方法;對于覆蓋的方法,運行時系統調用當前對象引用運行時類型中定義的方法。一個是調用編譯時類型中定義的方法,一個是調用運行時類型中定義的方法。通過下面這個例子可以直觀的看出兩者之間的差別。








































???Earth類覆蓋了Planet類的override方法,并隱藏了它的hide方法。在main方法中創建了一個Earth的實例,并將此實例轉換為一個Planet的引用。然后通過Planet引用調用override方法和hide方法。hide方法是類方法,因此根據引用的類來決定調用哪個方法,所以planet.hide()調用的是Planet的類方法hide。override方法是實例方法,因此調用的是運行時的實例中的方法,因為planet引用的是Earth的實例,因此planet.override()調用的是Earth類中實例方法override。
???JAVA正是通過實例方法的這種覆蓋關系來實現多態的目的。
三、類的變量在繼承過程中的問題:
???
???如果子類中的變量如果和父類中的變量具有相同的標記——即相同的名字——那么子類中的變量一就會隱藏父類中的變量。不過他們的類型是什么,也不管他們是類變量還是實例變量。只要他們具有相同的名字。

















???同子類中的類方法隱藏父類中的類方法一樣,當子類中的變量覆蓋父類中的變量的時候,到底使用哪個變量,要根據引用的類型來確定。
???下面是一個類成員繼承關系的綜合例子:



























































