父類宣告子類例項化以及過載,重寫的一些問題
最近面試碰到一道父類子類的面試題:
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()實現;如果不傳引數,那父類要麼有一個無引數的建構函式,要麼一個建構函式都沒有,不然就會編譯出錯,通俗一點說就是我可以接受你啥也沒有,但是不能有其他的但是沒我要的
所以兩個在二句例項化的時候,會先呼叫父類的構造方法。那麼也就是說,結果中會先顯示父類構造方法裡面的列印資料。
由於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之後,呼叫者就不知道被呼叫的程式碼是什麼,因為呼叫者拿到的是父類的引用,它不知道具體指向哪個子類的例項,更不知道要呼叫的方法具體是什麼,所以,被呼叫程式碼被偷偷換成另一個子類之後,呼叫者不需要做任何修改, 這就是解耦了。