Java中父類和子類中的方法呼叫和引數傳遞探討
阿新 • • 發佈:2019-01-29
有這樣一段程式,看看它會輸出什麼結果
public class Test {
public static void main(String [] args){
System.out.println(new B().getValue());
}
static class A{
protected int value;
public A(int v) {
setValue(v);
}
public void setValue(int value){
this.value = value;
}
public int getValue(){
try{
value++;
return value;
} catch(Exception e){
System.out.println(e.toString());
} finally {
this .setValue(value);
System.out.println(value);
}
return value;
}
}
static class B extends A{
public B() {
super(5);
setValue(getValue() - 3);
}
public void setValue(int value){
super.setValue(2 * value);
}
}
}
輸出:
22
34
17
這段程式咋一看沒什麼特殊的,但是細細分析下來,發現想搞清楚它的執行過程還真不簡單。通過Eclipse單步除錯,得出其執行流程如下:(序號1、2就表示執行次序)
public class Test {
public static void main(String[] args) {
System.out.println(new B().getValue());//1、程式開始
}
static class A {
protected int value;//
public A(int v) {//4、呼叫A的構造方法,v=5
setValue(v);//5、呼叫setValue方法(關鍵是呼叫A中的方法還是B中的?)
/*
* 這其實涉及到編譯時型別和執行時型別的問題,就setValue(v);這條語句而言,
* 呼叫這條語句的物件實際上是this。而this的編譯時型別是A(編譯時這條語句在A類中),
* 但是執行時型別卻是B。main方法中只有一條語句,我們分析的所有操作都是因為
* new B().getValue()這個語句引起的。所以執行時類的型別是B。那麼我們呼叫的自然
* 是B中的setValue方法。只有當B中沒有setValue方法時,才會呼叫父類A中的setValue方法。
*/
}
public void setValue(int value) {//8、子類B通過super顯示呼叫該方法,輸入引數value=10
this.value = value;//9、與第5步類似,這裡this還是指B類,但是B類中並沒有顯式定義名為value的成員,但是因為B類繼承了A,
//而A中的value修飾符為protect,所以B類也繼承了這個value成員。而且,這時A,B中持有的value引用是一樣的,是指向同一記憶體區域。
//換句話說,無論兩者誰修改了value的值,都會影響到對方。
//this.value=value是把B類中value值設為10,同時A中的value值受到影響,也變為10(因為是同一個value引用)
}
public int getValue() {//11、執行getValue方法,此時A的成員變數value值為10
try {
value++;//12、A的value值變為11,同時B中的value值變為11
return value;//13、返回value=11,但不會立即返回,會先執行finally語句塊
} catch (Exception e) {
System.out.println(e.toString());
} finally {//14、finally語句塊
this.setValue(value);//15、呼叫B的setValue方法
System.out.println(value);//16、輸出value值,此時A(B)中的value為22
/*
* finally語句塊執行完畢後會執行try語句塊中的return語句,雖然value的值變為22,但是getValue()的返回值仍然是11。
* 這是因為在try語句塊中的return語句執行時把返回值儲存在區域性變數裡,需要返回時再取出來。
* 但是假如在finally語句中也有return語句的話,返回值就是finally語句塊中的return返回值,而try語句塊中的返回值被覆蓋(失效)
*/
}
return value;//不會執行該句
}
}
static class B extends A {
public B() {//2、先呼叫B的構造方法
super(5);//3、呼叫父類A的構造方法並傳入引數5
setValue(getValue() - 3);//10、執行該句,首先計算getValue() - 3的值,這將呼叫A的getValue()方法。
//11、計算得到getValue() - 3=8,然後執行setValue(8),
}
public void setValue(int value) {//6、呼叫子類的setValue方法
super.setValue(2 * value);//7、通過super顯式呼叫父類的setValue方法,傳入引數10
}
}
}
通過對上述程式的分析,可以得到如下資訊:
1、當子類物件呼叫一個方法時,會優先在子類中查詢,然後才回去父類中查詢。如果子類呼叫了父類的方法method1,而method1又呼叫了method2,那麼這個method2還是會優先在子類中查詢,找不到才會去父類中查詢。
2、如果子類繼承了父類的成員變數(前提是可以繼承public和protected),而子類沒有顯示覆蓋該成員變數,那麼子類訪問到的成員變數和父類訪問到的成員變數是同一個變數。任何一個修改後都會影響到另一個。
3、try中的return會在finally語句塊之後執行,如果finally語句塊中有return語句,那個就直接返回這個return值。如果沒有,就返回try中的return值(finally塊中的任何非return操作都不會影響到try中的return值)。