如下所示代碼:
public class Example040 { private Example040 e40 = new Example040(); public Example040() throws Exception { throw new Exception("這裏是exception,不是error"); } public void output() { System.out.println("a new class"); } public static void main(String[] args) throws Exception { Example040 example040 = new Example040(); example040.output(); } }
代碼分析:
運行上述代碼,拋出了 StackOverflowError 異常。與大多數拋出 StackOverflowError 異常的程序一樣, 本程序也包含了一個無限遞歸。當你調用一個構造器時,實例變量的初始化操作將先於構造器的程序體而運行。在上述代碼中, e40變量的初始化操作遞歸調用了構造器,而該構造器通過再次調用構造器而初始化該變量自己的e40域,如此無限遞歸下去。這些遞歸調用在構造器程序體獲得執行機會之前就會拋出 StackOverflowError 異常,因爲 StackOverflowError是 Error 的子類型而不是 Exception 的子類型,所以 catch 子句無法捕獲它。給e40加上static修飾後,棧溢出異常就會消失了。
對上述代碼進行如下改造,加入兩個新的變量,新的變量初始化時會產生異常:
public class Example040 { // private Example040 e40 = new Example040(); private static Example040 e40 = new Example040(); private static Class internal = e40.new InternalClazz().getClass(); private InternalClazz clazz = (InternalClazz) internal.newInstance(); public Example040() /* throws Exception */{ // throw new Exception("這裏是exception,不是error"); } public void output() { System.out.println("a new class"); } class InternalClazz { public InternalClazz() { } } public static void main(String[] args) throws Exception { Example040 example040 = new Example040(); example040.output(); } }
上述程序將不能正確編譯,原因是:構造器必須聲明其實例初始化操作會拋出的所有被檢查異常。儘管其構造器沒有任何程序體,但是它將拋出兩個被檢查異常,InstantiationException 和IllegalAccessException。它們是 Class.newInstance拋出的,該方法是在初始化 clazz域的時候被調用的。
上述程序的一種改進方案如下所示,創建一個私有的、 靜態的助手方法,它負責計算域的初始值,並恰當地處理異常:
private InternalClazz clazz = newClazz(); private static InternalClazz newClazz() { try { return (InternalClazz) internal.newInstance(); } catch (InstantiationException | IllegalAccessException e) { e.printStackTrace(); } return null; }
總之, 實例初始化操作是先於構造器的程序體而運行的。 實例初始化操作拋出的任何異常都會傳播給構造器。如果初始化操作拋出的是被檢查異常,那麼構造器必須聲明也會拋出這些異常,但是應該避免這樣做,因爲它會造成混亂。最後,對於我們所設計的類,如果其實例包含同樣屬於這個類的其他實例,那麼對這種無限遞歸要格外當心。
注:本【java解惑】系列均是博主閱讀《java解惑》原書後將原書上的講解和例子部分改編然後寫成博文進行發佈的。所有例子均親自測試通過並共享在github上。通過這些例子激勵自己惠及他人。同時本系列所有博文會同步發佈在博主個人微信公衆號搜索“愛題猿”或者“ape_it”方便大家閱讀。如果文中有任何侵犯原作者權利的內容請及時告知博主以便及時刪除如果讀者對文中的內容有異議或者問題歡迎通過博客留言或者微信公衆號留言等方式共同探討。
源代碼地址https://github.com/rocwinger/java-disabuse