1. 程式人生 > >父類宣告子類例項化以及過載,重寫的一些問題

父類宣告子類例項化以及過載,重寫的一些問題

最近面試碰到一道父類子類的面試題:

public class SuperClass {
    public int a;
    public SuperClass(){
        a=1;
        System.out.println("a is"+a);
    }
    public int getA(){
        return a;
    }
}
public class SubClass extends SuperClass {
     public int a =2;
     public SubClass(){
          System.out.println("a is"+a);
     }
     public int getA(){
          return a;
     }

     public static void main(String[] args) {
          SuperClass aClass = new SuperClass();
          SuperClass bClass = new SubClass();
          System.out.println("num1 is "+(aClass.a+bClass.a));
          System.out.println("num2 is "+(aClass.getA()+bClass.getA()));
          System.out.println("num3 is "+(aClass.a+bClass.getA()));
          System.out.println("num4 is "+(aClass.getA()+bClass.a));

     }
}

問輸出的結果?

 這題一看就知道輸出的四個結果肯定不同。

本身涉及到繼承的知識,也就是重寫的知識,不過這部分比較好理解,就是子類繼承於父類,重寫的父類的方法,那麼呼叫的時候就呼叫子類自己的方法。

但是這道題噁心的地方在於bClass採用了父類宣告子類例項化,那麼bClass裡面的引數到底是什麼樣的呢?

首先我們知道java繼承中,通過繼承,子類可以得到父類除建構函式以外所有的成員(包括成員變數和成員函式),但是要注意得到並不等於可以隨便使用。子類能否使用(訪問)父類的成員由父類成員的屬性決定。

對於子類物件來說,在呼叫自身類的建構函式之前會先呼叫父類的建構函式

。第一種情況:若子類建構函式向父類建構函式傳遞引數(通過super()在建構函式第一行實現),那麼會自動呼叫父類有對應引數的建構函式;第二種情況:子類建構函式沒有引數傳遞給父類建構函式,這種情況下系統會隱式呼叫父類無引數的建構函式,倘若父類沒有任何建構函式,編譯正常;若父類有帶引數的建構函式而沒有無引數的建構函式,編譯時會發生錯誤。

對剛才的呼叫情況小結一下:無論如何,子類建構函式都會先呼叫父類建構函式,如果要傳引數,通過super()實現;如果不傳引數,那父類要麼有一個無引數的建構函式,要麼一個建構函式都沒有,不然就會編譯出錯,通俗一點說就是我可以接受你啥也沒有,但是不能有其他的但是沒我要的

所以兩個在二句例項化的時候,會先呼叫父類的構造方法。那麼也就是說,結果中會先顯示父類構造方法裡面的列印資料。

由於bClass是通過父類宣告子類例項化的方式定義的。

bClass這個例項是子類的,但是因為你宣告時是用父類宣告的,所以你用正常的辦法訪問不到子類自己的成員,只能訪問到從父類繼承來的成員。

在子類中重寫父類中方法時,例項化父類呼叫該方法,執行時呼叫的是子類中重寫的方法;

所以bClass.a=1,bClass.getA()返回2

所以最後的結果是:

我在程式碼中加入列印各項資訊:

public class SuperClass {
    public int a;
    private int c;
    protected int d;
    public SuperClass(){
        a=1;
        c=5;
        System.out.println("a is"+a);
    }
    public int getA(){
        return a;
    }

    public int getC() {
        return c;
    }

    public int getD() {
        return d;
    }
}

public class SubClass extends SuperClass {
    public int a =2;
    private int c=6;
    protected int d =9;
    public SubClass(){
        System.out.println("a is"+a);
    }
    public int getA(){
        return a;
    }

    public int getC() {
        return c;
    }

    public int getD() {
        return d;
    }

    public static void main(String[] args) {
        SuperClass aClass = new SuperClass();
        SuperClass bClass = new SubClass();
        System.out.println("num1 is "+(aClass.a+bClass.a));
        System.out.println("num2 is "+(aClass.getA()+bClass.getA()));
        System.out.println("num3 is "+(aClass.a+bClass.getA()));
        System.out.println("num4 is "+(aClass.getA()+bClass.a));

        System.out.println("b.a is "+bClass.a);
        System.out.println("b.getA is "+bClass.getA());

        System.out.println("b.getC is "+bClass.getC());

        System.out.println("b.d is "+bClass.d);
        System.out.println("b.getD is "+bClass.getD());


        System.out.println("a.a is "+aClass.a);
        System.out.println("a.getA is "+aClass.getA());
        SubClass cClass = new SubClass();
        System.out.println("c.a is "+cClass.a);
        System.out.println("c.getA is "+cClass.getA());




    }
}

列印的結果:

 

 這裡面會涉及到一個依賴倒置原則(DIP)

 

依賴倒置原則,DIP,Dependency Inverse Principle DIP的表述是: 
1、高層模組不應該依賴於低層模組,二者都應該依賴於抽象。 2、抽象不應該依賴於細節,細節應該依賴於抽象。 這裡說的“依賴”是使用的意思,如果你呼叫了一個類的一個方法,就是依賴這個類,如果你直接呼叫這個類的方法,就是依賴細節,細節就是具體的類,但如果你呼叫的是它父類或者介面的方法,就是依賴抽象, 所以DIP說白了就是不要直接使用具體的子類,而是用它的父類的引用去呼叫子類的方法,這樣就是依賴於抽象,不依賴具體。

其實簡單的說,DIP的好處就是解除耦合,用了DIP之後,呼叫者就不知道被呼叫的程式碼是什麼,因為呼叫者拿到的是父類的引用,它不知道具體指向哪個子類的例項,更不知道要呼叫的方法具體是什麼,所以,被呼叫程式碼被偷偷換成另一個子類之後,呼叫者不需要做任何修改, 這就是解耦了。