Java中的繼承、組合和代理
一、介紹
繼承是OOP語言必不可少的一部分,當建立一個類時我們總是在繼承,因為所有類都是Objiect的子類,同時,你也可以自己選擇你想要繼承的類,但簡單的繼承在實際應用中可能會出現一些問題,所以就出現了組合和代理,下面我們來深入瞭解一下它們之間的區別:
二、三者之間的區別
1、繼承
一個類可以利用extends關鍵字顯式的繼承另一個類,而被繼承的類稱為父類,繼承該類的類稱為子類,如:
1 class animal { 2 public void eat{ 3 System.out.println("x"); 4 } 5 } 6 class Dog extendsanimal{ 7 String name; 8 }
如以上程式碼,animal被Dog繼承,此時animal稱為父類(基類),而Dog稱為animal的子類(匯出類),如果兩個類存在繼承關係,則子類會自動繼承父類的方法和變數,在子類中可以呼叫父類的方法和變數。在java中,只允許單繼承,也就是說 一個類最多隻能顯示地繼承於一個父類。但是一個類卻可以被多個類繼承,也就是說一個類可以擁有多個子類。
子類繼承父類的成員變數:
當子類繼承了某個類之後,便可以使用父類中的成員變數,但是並不是完全繼承父類的所有成員變數。具體的原則如下:
1)能夠繼承父類的public和protected成員變數;不能夠繼承父類的private成員變數;
2)對於父類的包訪問許可權成員變數,如果子類和父類在同一個包下,則子類能夠繼承;否則,子類不能夠繼承;
3)對於子類可以繼承的父類成員變數,如果在子類中出現了同名稱的成員變數,則會發生隱藏現象,即子類的成員變數會遮蔽掉父類的同名成員變數。如果要在子類中訪問父類中同名成員變數,需要使用super關鍵字來進行引用。
子類繼承父類的方法:
同樣地,子類也並不是完全繼承父類的所有方法。
1)能夠繼承父類的public和protected成員方法;不能夠繼承父類的private成員方法;
2)對於父類的包訪問許可權成員方法,如果子類和父類在同一個包下,則子類能夠繼承;否則,子類不能夠繼承;
3)對於子類可以繼承的父類成員方法,如果在子類中出現了同名稱的成員方法,則稱為覆蓋,即子類的成員方法會覆蓋掉父類的同名成員方法。如果要在子類中訪問父類中同名成員方法,需要使用super關鍵字來進行引用。
注意:隱藏和覆蓋是不同的。隱藏是針對成員變數和靜態方法的,而覆蓋是針對普通方法的。
覆蓋只有在某方法是基類的介面的一部分時才會出現,如果一個基類的方法是private,那它就不是介面的一部分,也就是說子類是無法繼承的,現在子類中出現一個同名方法時,就不叫覆蓋。
構造器:
子類是不能夠繼承父類的構造器,但是要注意的是,如果父類的構造器都是帶有引數的,則必須在子類的構造器中顯示地通過super關鍵字呼叫父類的構造器並配以適當的引數列表。如果父類有無參構造器,則在子類的構造器中用super關鍵字呼叫父類構造器不是必須的,如果沒有使用super關鍵字,系統會自動呼叫父類的無參構造器。看下面這個例子就清楚了:
1 class x{ 2 x(){ 3 System.out.println("x"); 4 } 5 } 6 class y extends x{ 7 y(){ 8 System.out.println("y"); 9 } 10 } 11 class z extends y{ 12 z(){ 13 System.out.println("z"); 14 } 15 16 public static void main(String[] args) { 17 z test = new z(); 18 } 19 }
輸出:
x
y
z
上述程式碼是預設呼叫父類的無參構造,還可以利用super呼叫父類的指定構造器,注意super需要在構造器內書寫:
1 class x{ 2 x(){ 3 System.out.println("x"); 4 } 5 x(int x){ 6 System.out.println("x"+x); 7 } 8 } 9 class y extends x{ 10 y(){ 11 System.out.println("y"); 12 } 13 y(int x){ 14 super(1);//呼叫父類有參構造 15 System.out.println("y"+x); 16 } 17 } 18 class z extends y{ 19 z(){ 20 super(1);//呼叫父類有參構造 21 System.out.println("z"); 22 } 23 24 public static void main(String[] args) { 25 z test = new z(); 26 } 27 }
輸出:
x1
y1
z
super主要有兩種用法:
1)super.成員變數/super.成員方法;
2)super(parameter1,parameter2....)
第一種用法主要用來在子類中呼叫父類的同名成員變數或者方法;第二種主要用在子類的構造器中顯示地呼叫父類的構造器,要注意的是,如果是用在子類構造器中,則必須是子類構造器的第一個語句。
2、組合
組合的邏輯其實很簡單,如果你在a類中需要使用到b類的方法或者變數,就可以在a類中直接建立一個b類的物件,然後利用這個物件直接呼叫其中的方法,如:
class x { public void x1(){ System.out.println("this is x"); } } class y{ x x1 = new x(); public void y1{ x1.x1(); } }
可以看到我們我們可以利用這個方法去呼叫x1類裡的方法,但很容易發現一個問題,當我們在y類建立一個x的物件,那麼x裡的所有東西在y裡都是透明的,這是和繼承的區別之一,繼承只能繼承那些父類想要你看到的東西,有很高的靈活性。
組合中物件的初始化:我們在一個類中定義了另一個類的物件,這就涉及到這個物件初始化的問題,可以有三種方法:
1、在定義物件處進行初始化
2、在類的構造器中進行初始化
3、在使用該物件之前進行初始化,這種稱為惰性初始化,在生成物件時沒必要每次都初始化,這樣可以減少額外的負擔
如果不進行初始化,這個類物件會自動被編譯器賦值一個null,與其他成員變數一樣,如int的初值為0,等待編寫者給它進行操作
3、代理
代理Java並沒有提供給它的直接支援,它是介於繼承和組合之間的中庸之道,代理同樣將一個成員物件放置於所要構造的類中(就像組合一樣),但不同的是,代理將成員物件設定為私有的,然後另寫方法去呼叫這個成員的必要方法,從而實現對基類所有成員的適當保護。具體可以看我的文章:https://www.cnblogs.com/kxxiaomutou/p/15616068.html
11