1. 程式人生 > >Java使用繼承時需要注意的二三事

Java使用繼承時需要注意的二三事

    繼承是實現類複用的重要手段,它能夠有效減少重複程式碼的數量,但同時也帶來一個最大的壞處:破壞封裝

    子類拓展父類時,子類從父類繼承得到了成員變數和方法,如果訪問許可權允許,子類可以直接訪問父類的成員變數和方法,相當於子類可以直接複用父類的成員變數和方法,十分便利。

    但是在繼承關係中,子類可以直接訪問父類的內部資訊和方法,導致了父類和子類的嚴重耦合。從這個角度看,父類的實現細節對子類不再透明,子類可以訪問父類的成員變數和方法,可以隨意改變父類的實現細節(比如通過方法重寫來改變父類的實現方法),從而導致子類可以惡意篡改父類的方法。

    示例如下:

public class Main {
    public static void main(String[] args){
        person p = new student();
        p.talk();//最終輸出I'm a student
    }
}
class person{
    public void talk(){
        System.out.print("I'm a person");
    }
}

class student extends person{
    public void talk(){
        System.out.print("I'm a student");
    }
}

    可以看到,儘管上面申明的是person引用變數,但是實際上引用的是一個student物件。所以在呼叫p的talk()方法時執行的不再是person的talk()方法,而是student的talk()方法。

    為了保證良好的封裝性,不會被子類隨意更改,設計父類時應該遵循如下規則:

  • 儘量隱藏父類的內部資料。把父類的所有成員變數設定為private訪問型別,不要讓子類直接訪問父類的成員變數。
  • 不要讓子類隨意訪問、修改父類的方法。父類中的那些僅為輔助其他的工具方法應該使用private訪問控制符修飾。如果父類中的方法需要被外部類呼叫,但又不希望子類重寫,則要使用final修飾符來修飾該方法。如果希望該方法被子類重寫但不被外部類呼叫,則應該使用protected來修飾該方法。
  • 儘量不要在父類構造器呼叫要被子類重寫的方法。 關於最後一點,我們使用一個例子來演示:
public class Main {
    public static void main(String[] args){
        student p = new student();//輸出子類重寫父類方法子類重寫父類方法
    }
}
class person{
    public void talk(){
        System.out.print("父類方法");
    }
    public person(){
        talk();
    }
}

class student extends person{
    public void talk(){
        System.out.print("子類重寫父類方法");
    }
    public student(){
        talk();
    }
}

    當系統嘗試建立student物件時,會執行其父類構造器,如果父類構造器呼叫了被其子類重寫的方法,則變成呼叫被子類重寫的方法。

    如果想把某些類設定為最終類,即不能被當成父類,則可以使用final修飾這個類。除此之外,使用private修飾這個類的所有構造器也可以使得子類無法呼叫該類的構造器,也就無法繼承該類。對於把所有構造器使用private修飾的父類而言,可以另外提供一個靜態方法,用於建立該類例項。

    那麼究竟在什麼時候需要從父類派生新的子類呢?

    不僅需要保證子類是一種特殊的父類,還要具備以下兩個條件之一:

  • 子類需要增加額外的屬性,而不僅僅是屬性值的改變。
  • 子類需要增加自己的獨特行為(包括增加新的方法或者重寫父類方法)