Java併發程式設計實踐-this溢位

qq_42938846發表於2019-01-14

   《Java併發程式設計實踐》一書中,提到“在建構函式中呼叫一個可覆蓋的(那些既不是private,也不是final的)例項方法同樣會導致this引用在構造期間溢位。”,這句話讀來簡單,但是卻乾貨滿滿,這裡舉一個簡單的示例來進行說明。

ThisEscape類:

public class ThisEscape {	
	public ThisEscape() {
		System.out.println("ThisEscape ");
		// 在構造方法中其呼叫了非final和private的方法
		Say();
	}

	public void Say() {
		System.out.println("ThisEscape .Say()");
	}

	public static void main(String[] args) {
		new ThisEscapeSon();
		new ThisEscapeSon("Hello World");
	}
}

ThisEscapeSon.class類
ThisEscapeSon繼承自ThisEscape類並且重寫了Say方法。

 class ThisEscapeSon extends ThisEscape {
	private final String name;

	public ThisEscapeSon() {
		this.name = "Hello Word";
		System.out.println("ThisEscapeSon");
	}

	public ThisEscapeSon(String name) {
		this.name = name;
		System.out.println("ThisEscapeSon");
	}

	/*
	 * 在子類中重寫Say方法
	 */
	@Override
	public void Say() {
		System.out.println("ThisEscapeSon.Say()-->" + name);
	}
}

執行結果

ThisEscape
ThisEscapeSon.Say()-->null
ThisEscapeSon
ThisEscape
ThisEscapeSon.Say()-->null
ThisEscapeSon

   分析:無論是呼叫子類(ThisEscapeSon)的預設建構函式還是帶參構造器來例項化ThisEscapeSon例項,預設情況下它都會首先依次呼叫父類的無參構造方法(相當於建構函式第一行程式碼處呼叫父類的無參構造器)例項化父類,接著才會執行子類構造器的其他方法。
   所以,當執行到父類ThisEscape構造器中執行Say()方法時,此時由於子類已經重寫了Say()方法,他就會呼叫子類的Say()方法,實際上,在這裡就可以預見this溢位的後果了,由於子類中的this.name語句還沒有執行,所以,執行Say()方法時name成員變數並沒有被賦值,所以會列印null值。
    總結:父類構造方法呼叫的非final、private方法,如果存在子類的情況下,對於該方法的執行就會具有不可預見性的問題,這在程式設計過程需要極度注意的。
    修改方式:
1)將ThisEscapeSay()方法改成private作用域,那麼,子類ThisEscapeSon就不能重寫該方法,而只是重新定義了Say()方法,此時,只會呼叫ThisEscapeSay()方法完成相應的功能。
2)將ThisEscapeSay()方法改成final作用域,那麼,如果子類定義Say(),就會報編譯器錯誤,從而防止類似不可以預見性事件的發生。

相關文章