Java自學十四課之類的繼承之自學筆記
類的繼承
1.繼承的概念 繼承是一種有已有類建立新類的機制。一個新類從現有的類派生出來,現有的類叫父類或超類,新類稱為子類。 繼承的好處:
- 提高了程式碼的複用性;
- 提高了程式碼的維護性;
- 提高累的抽象程度,是指更接近於人類的思維方式。
- 讓類與類之間產生了關係,是多型的前提
繼承的弊端:
- 類的耦合性增強了;
- 開發的原則:高內聚,低耦合。 耦合:類與類的關係 內聚:就是自己完成某件事情的能力
Java要求宣告的每一個類都有父類,當沒有顯式的指出父類時,父類隱含為Java.lang包中的Object類。這個類是所有類的直接或間接父類。一個父類可以擁有多個子類,這個父類就是所有子類都有的方法和屬性的集合。每一個子類都是父類的特殊化,具體化。
Java單繼承。
繼承的語法
【類修飾符】 class ClassName 【extends SuperClassName】 【implement interface】{ //類體 } 其中extends是集繼承的關鍵字。 繼承中需要注意的是,對於父類中的私有屬性和方法。 對於私有屬性和方法有兩種說法:
- 私有屬性和方法不能被繼承,但可以通過公有方法去訪問私有屬性。官方文件也是這麼解釋的。“A subclass does not inherit the private members of its parent class. However, if the superclass has public or protected methods for accessing its private fields, these can also be used by the subclass.”
- 私有屬性也可以繼承,但是由於修飾符是private和protected的,,所有對外是不可見的,所以可以用公有方法訪問。
覆蓋和隱藏
1.隱藏 子類對從父類繼承來的屬性變數加以重新定義,則從父類繼承的屬性將被隱藏。 這樣子類中就擁有了一個和父類中變數名字一樣的變量了,這時候當子類執行繼承自父類的操作時,處理的是繼承自父類的變數,而當子類執行它自己宣告的方法時,所操作的就是它自己宣告的變數,而把繼承自父類的變數給隱藏了。預設情況下使用的是子類的變數。 上面說了①執行繼承自父類的操作時,用的是父類的變數,還有一種方法②可以在子類中使用super.變數名來訪問父類的變數。但是必須放在第一行。 這裡有一個很好的例子:
package com.ann.test;
import javax.management.remote.SubjectDelegationPermission;
public class BreakLabel {
public static void main(String[] args) {
// TODO Auto-generated method stub
A a=new A();
a.setx(4);
a.printa();
B b=new B();
b.printb();
b.printa();
b.setx(6);
b.printb();
b.printa();
a.printa();
}
}
class A{
int x=2;
public void setx(int i){
x=i;
}
void printa(){
System.out.println(x);
}
}
class B extends A{
int x=100;
void printb(){
super.x=super.x+10;
System.out.println("super.x="+super.x+" x="+x);
}
}
結果: 4 super.x=12 x=100 12 super.x=16 x=100 16 4
分析一下:1、new了一個類A的物件; 2、父類物件執行父類方法,給x賦為4; 3、父類物件呼叫父類輸出的方法,這時候a物件的x被改為4了; 4、new一個子類B的物件; 5、子類物件執行子類方法,方法中使用super.x使用繼承的x,這時候要注意剛才的4對a物件的屬性值進行修改的,所以這時候的super.x是被隱藏了的x=2。所以執行完後輸出的是12和100; 6、子類物件呼叫繼承的父類方法,這裡就是前面的說執行的是來自父類的操作時,用的是父類的變數,這裡父類的變數是super.x,剛剛改變了super.x=12,所以輸出12。為什麼不是14呢?是因為4是a物件的屬性值。這裡是另外一個物件b了; 7、B類物件b呼叫繼承自父類的方法,所用的變數也就是繼承自父類的變數,也就是super.x=2,然後改變這個值為6。 8、子類物件呼叫子類方法,和第6步一樣,,只是這裡的super.x不是2了,是剛剛賦值的6,之前不能用4是因為4是a物件的,呼叫時用的是b物件呼叫的。而這裡可以用6是因為設定值和呼叫這個方法都是b物件呼叫的。所以輸出16和100; 9、子類物件呼叫繼承自父類的方法,所以用的變數是來自父類的變數super.x,這裡的super.x剛剛被改成了16,所以輸出16。 10、父類物件a呼叫自己的方法,輸出x,因為之前給a物件的x賦值為4,所以輸出是4。
注意,之前自己矛盾的有以下幾點,①明明賦值為4了,為什麼super.x用的是2來加10,是因為,賦值的4是a物件的,super只是一個單獨的從父類繼承的物件,沒有依賴於物件,所以是2。②super.x雖然是來自父類的變數,但其實是算自己類的變數,加上super只是說這是來自父類的變數,和子類同名的變數加以區別而已。這就是自己類的變數,這點要牢記,所以執行了super.x=super.x+10後,super.x就變了,下次不會再去從父類中取x。而且要記住,子類相應物件改變了super.x,下次這個物件的super.x一定要記得變。
另外有一個重要的點是:如果想子父類都可以改變一個變數的話,可以將這個變數設定成static。需要注意的是,子類可以繼承父類的靜態屬性和方法,但不能重寫。 例子如下: 父(Fu)類:
package com.test;
public class Fu {
public static void show() {
System.out.println("父類的靜態方法");
}
public void method() {
System.out.println("父類的一般方法");
}
}
---------------------
作者:jianhaojiang
來源:CSDN
原文:https://blog.csdn.net/qq_38584967/article/details/78504131
版權宣告:本文為博主原創文章,轉載請附上博文連結!
子(Zi)類:
package com.test;
public class Zi extends Fu {
public static void main(String[] args) {
Zi zi = new Zi();
zi.show();
zi.method();
}
public static void show() {
System.out.println("子類的靜態");
}
public void method() {
System.out.println("子類的一般方法");
}
}
---------------------
作者:jianhaojiang
來源:CSDN
原文:https://blog.csdn.net/qq_38584967/article/details/78504131
版權宣告:本文為博主原創文章,轉載請附上博文連結!
輸出結果是: 子類的靜態方法 子類的一般方法
這樣的程式碼並不能看出來是不是都重寫成功的,子類例項化的自己呼叫自己的方法很正常,並不能看出什麼
!!!劃重點了!!!
我們用父類物件接收試試,Fu類不變,修改Zi類:
package com.test;
public class Zi extends Fu {
public static void main(String[] args) {
Fu fu = new Zi();
fu.show();
fu.method();
}
public static void show() {
System.out.println("子類的靜態");
}
public void method() {
System.out.println("子類的一般方法");
}
}
---------------------
作者:jianhaojiang
來源:CSDN
原文:https://blog.csdn.net/qq_38584967/article/details/78504131
版權宣告:本文為博主原創文章,轉載請附上博文連結!
輸出結果是: 父類的靜態方法 子類的一般方法
So,看出來了沒,我們清楚的看到用父類物件去接收子類例項化物件呼叫方法的話,結果普通方法輸出子類zi的一般方法,表明子類成功重寫並且覆蓋了父類普通方法,但是靜態方法輸出的還是父類fu的方法,並沒有重寫覆蓋。 表明子類可以繼承父類靜態方法但是不能重寫,但是完全是可以繼承的,現在刪除子類定義的show()方法程式碼,輸出同樣不變(子類先在自己成員函式找show(),沒有找到,然後就去父類找,在父類找到了)說明確實是可以繼承的
package com.test;
public class Zi extends Fu {
public static void main(String[] args) {
Fu fu = new Zi();
fu.show();
fu.method();
}
public void method() {
System.out.println("子類的一般方法");
}
}
輸出結果是:
父類的靜態方法
子類的一般方法
---------------------
作者:jianhaojiang
來源:CSDN
原文:https://blog.csdn.net/qq_38584967/article/details/78504131
版權宣告:本文為博主原創文章,轉載請附上博文連結!
好了,接下來是我的理解: 所謂靜態就是指:在編譯時所分配的記憶體會一直存在(不會被回收),直到程式退出記憶體才會釋放這個空間,在例項化之前這個方法就已經存在於記憶體,跟類的物件沒什麼關係。子類中如果定義了相同名稱的靜態方法,並不會重寫,而應該是在記憶體中又分配了一塊給子類的靜態方法,沒有重寫這一說,只是單純的名字問題。
2.覆蓋(重寫) 如果子類不需要從父類繼承來的方法,可以自己重新宣告,但是宣告的方法必須使用相同的方法名、引數列表,但是執行不用的功能,這就叫方法的覆蓋,也叫重寫。 關鍵詞super可以呼叫父類的方法。比如super.asd();必須在第一行。
有繼承時的構造方法
構造方法可以被過載,但不能從父類那裡繼承 有繼承時的構造方法遵循的原則:
- 子類不能從父類繼承構造方法;
- 好的程式設計方法是在子類的構造方法中呼叫父類的某一個構造方法;
- super關鍵字也可以泳衣構造方法中,其功能為呼叫父類的構造方法;
- 如果在子類的構造方法的宣告中沒有明確呼叫父類的構造方法,則系統在執行子類的構造方法時會自動呼叫父類的預設的構造方法;
- 如果在子類的構造方法的宣告中呼叫父類的構造方法,則呼叫語句必須出現在子類構造方法的第一行。
這裡如果運用到this呼叫某一個構造方法,那麼可以寫在無參的構造方法,呼叫有參的方法,這樣在子類就可以不用顯式的呼叫,預設的呼叫父類無參的構造方法。