1. 程式人生 > >Java中父類和子類中的方法呼叫和引數傳遞探討

Java中父類和子類中的方法呼叫和引數傳遞探討

有這樣一段程式,看看它會輸出什麼結果

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值)。