我們知道在通過new關鍵字生成對象時必然會調用構造函數,構造函數的簡繁情況會直接影響執行個體對象的建立是否繁瑣。在項目開發中,我們一般都會制訂構造函數盡量簡單,盡可能不抛異常,盡量不做複雜算法等規範,那如果一個構造函數确實複雜了會怎麼樣?我們來看一段代碼:
該代碼是一個服務類的簡單模拟程式,Server類實作了伺服器的建立邏輯,子類隻要在生成執行個體對象時傳遞一個端口号即可建立一個監聽該端口的服務,該代碼的意圖如下:
通過SimpleServer的構造函數接收端口參數。
子類的構造函數預設調用父類的構造函數。
父類構造函數調用子類的getPort方法獲得端口号。
父類構造函數建立端口監聽機制。
對象建立完畢,服務監聽啟動,正常運作。
貌似很合理,再仔細看看代碼,确實也和我們的意圖相吻合,那我們嘗試多次運作看看,輸出結果要麼是“端口号:40000”,要麼是“端口号:0”,永遠不會出現“端口号:100”或是“端口号:1000”,這就奇怪了,40000還好說,但那個0是怎麼冒出來的呢?代碼在什麼地方出現問題了?
要解釋這個問題,我們首先要說說子類是如何執行個體化的。子類執行個體化時,會首先初始化父類(注意這裡是初始化,可不是生成父類對象),也就是初始化父類的變量,調用父類的構造函數,然後才會初始化子類的變量,調用子類自己的構造函數,最後生成一個執行個體對象。了解了相關知識,我們再來看上面的程式,其執行過程如下:
子類SimpleServer的構造函數接收int類型的參數:1000。
父類初始化常變量,也就是DEFAULT_PORT初始化,并設定為40000。
執行父類無參構造函數,也就是子類的有參構造中預設包含了super()方法。
父類無參構造函數執行到“int port = getPort()”方法,調用子類的getPort方法實作。
子類的getPort方法傳回port值(注意,此時port變量還沒有指派,是0)或DEFAULT_PORT(此時已經是40000)了。
父類初始化完畢,開始初始化子類的執行個體變量,port指派100。
執行子類構造函數,port被重新指派為1000。
子類SimpleServer執行個體化結束,對象建立完畢。
終于清楚了,在類初始化時getPort方法傳回的port值還沒有指派,port隻是獲得了預設初始值(int類的執行個體變量預設初始值是0),是以Server永遠監聽的是40000端口了(0端口是沒有意義的)。這個問題的産生從淺處說是由類元素初始化順序導緻的,從深處說是因為構造函數太複雜而引起的。構造函數用作初始化變量,聲明執行個體的上下文,這都是簡單的實作,沒有任何問題,但我們的例子卻實作了一個複雜的邏輯,而這放在構造函數裡就不合适了。
問題知道了,修改也很簡單,把父類的無參構造函數中的所有實作都移動到一個叫做start的方法中,将SimpleServer類初始化完畢,再調用其start方法即可實作伺服器的啟動工作,簡潔而又直覺,這也是大部分JEE伺服器的實作方式。
注意 構造函數簡化,再簡化,應該達到“一眼洞穿”的境界。
本文轉自SummerChill部落格園部落格,原文連結:http://www.cnblogs.com/DreamDrive/p/5428686.html,如需轉載請自行聯系原作者