1. 程式人生 > 其它 >Java中的繼承、組合和代理

Java中的繼承、組合和代理

一、介紹

  繼承是OOP語言必不可少的一部分,當建立一個類時我們總是在繼承,因為所有類都是Objiect的子類,同時,你也可以自己選擇你想要繼承的類,但簡單的繼承在實際應用中可能會出現一些問題,所以就出現了組合和代理,下面我們來深入瞭解一下它們之間的區別:

二、三者之間的區別

1、繼承

  一個類可以利用extends關鍵字顯式的繼承另一個類,而被繼承的類稱為父類,繼承該類的類稱為子類,如:

1 class animal {
2     public void eat{
3         System.out.println("x");
4     }
5 }
6 class Dog extends
animal{ 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