Java對象初始化順序

来源:http://www.cnblogs.com/shewu/archive/2016/10/19/5976629.html
-Advertisement-
Play Games

最近我發現了一個有趣的問題,這個問題的答案乍一看下騙過了我的眼睛。看一下這三個類: package com.ds.test; public class Upper { String upperString; public Upper() { Initializer.initialize(this); ...


最近我發現了一個有趣的問題,這個問題的答案乍一看下騙過了我的眼睛。看一下這三個類:

package com.ds.test;
 
public class Upper {
String upperString;
 
public Upper() {
  Initializer.initialize(this);
}
}

package com.ds.test;
 
public class Lower extends Upper {
 
String lowerString = null;
 
public Lower() {
  super();
  System.out.println("Upper:  " + upperString);
  System.out.println("Lower:  " + lowerString);
}
 
public static void main(final String[] args) {
  new Lower();
}
}

package com.ds.test;
public class Initializer {
static void initialize(final Upper anUpper) {
  if (anUpper instanceof Lower) {
   Lower lower = (Lower) anUpper;
   lower.lowerString = "lowerInited";
  }
  anUpper.upperString = "upperInited";
}
}

運行 Lower 這個類可以得到什麼輸出?在這個極簡的例子中可以更容易地看到整個形勢,但是這個情形發生在現實中會有非常多的代碼分散一個人的註意力。

不管怎麼樣,輸出是像這樣的:

Upper:  upperInited
Lower:  null;

雖然小示例中使用了 String 類型,Initializer 類的實際代碼中有一個用於註冊的委托對象,與 Lower 類的功能是相同的 — 至少 Lower 類是這個意圖。但由於某些原因在運行應用程式時沒有工作。取而代之的是,使用了預設路徑,委托對象沒有被設置 (null)。

現在稍微改變一下 Lower 的代碼:

package com.ds.test;
 
public class Lower extends Upper {
 
String lowerString;
 
public Lower() {
  super();
  System.out.println("Upper:  " + upperString);
  System.out.println("Lower:  " + lowerString);
}
 
public static void main(final String[] args) {
  new Lower();
}
}

現在的輸出是這樣的:

Upper:  upperInited
Lower:  lowerInited

發現代碼中的區別了嗎?

是的,這個 lowerString 欄位不再明確地設置為空。為什麼這麼做會有不同。不管怎樣參考類型欄位(例如這裡的 String )的預設值不是為空的嗎?當然是空的。事實證明,雖然這種微小的變化顯然不會以任何方式改變代碼行為,但是卻讓結果變的不同。

那麼,到底發生了什麼?當查看初始化順序的時候一切就變的清晰了:

1.main() 函數調用了 Lower 構造器。

2.Lower 的一個實例被準備好了。意味著所有的欄位都被創建並且填充了預設值,例如,引用類型的預設值為空,布爾類型的預設值為 false 。在這個時候,任何的對欄位的內聯賦值都沒有發生。

3.父類構造器被調用了。這是被語言的特性所強制執行的。所以在其他任何事發生之前,Upper 的構造器被調用了。

4.Upper 這個構造器運行並且指定了一個引用,指向 Initializer.initialize() 方法新創建的的實例。

5.Initializer 類為兩個欄位( upperString 和 lowerString )附上新字元串。通過使用有點骯髒的 instanceof 實例檢查做到為那兩個欄位賦值 – 這不是一個特別好的設計模式,但是也有可行的,不用管那麼多。一旦發生了,upperString 和 lowerString 的引用都不再為空。

6.Initializer.initialize() 的調用完成,Upper 構造器也同樣完成。

7.現在變得有趣了:Lower 實例的構造在繼續。假設在 lowerString 欄位的聲明中沒有明確地 =null 賦值,Lower 構造器恢復執行並且列印出兩個連接到欄位的字元串。

然而,如果有一個明確地賦值 null 的操作,執行流程會略有不同:當父類構造器完成後,在其餘的構造器運行前,任何變數初始化都會執行(參見java語言規範12.5節)。在這種情況下,之前賦值給 lowerString 的字元串引用會再一次被賦予 null 。然後繼續執行其餘的函數構造,現在列印 lowerString 的值為: null 。

這是一個很好的例子,不僅方便我們如何註意一些創建對象的細節(或者知道去哪裡查看 Java 編碼規範,列印的或者線上的),還顯示了為什麼像這樣寫初始化是很糟糕的。我們一點都不應該關心 Upper 的子類。相反的,如果因為一些原因對某些欄位的初始化不能在子類本身被完成,它將只需要它自己的某些初始化幫助類的變體。在這種情況下,如果你使用 String lowString 或者 String lowerString = null 是真的沒有任何區別的,它應該是什麼就會是什麼。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 前言 本篇文章介紹ASP.NET Core里,用來處理HTTP封包的Middleware,為自己留個紀錄也希望能幫助到有需要的開發人員。 ASP.NET Core官網 結構 在ASP.NET Core里,每個從「瀏覽器傳入」的HTTP Request封包,會被系統封裝為「HttpRequest對象」 ...
  • 上一章筆者講到關於IO文件操作類,瞭解如何處理文件流。從這一章開始筆者將講解相對比較高級的知識點。而本章筆者就對WinForm開發的知識點進行講解和引導。現在很多業務都是面向於B/S模式的開發,JAVA也不另外。所以JAVA的程式員對J2EE部分的知識非常瞭解。卻對J2SE的知識點半知半解。甚至有一 ...
  • error: invalid preprocessing directive #difine| 無效的巨集定義處理 巨集定義define 寫成了 difine。 ...
  • 英文文檔: bin(x) Convert an integer number to a binary string. The result is a valid Python expression. If x is not a Python int object, it has to define ...
  • 英文文檔: any(iterable) Return True if any element of the iterable is true. If the iterable is empty, return False. Equivalent to: 說明: 1. 接受一個可迭代器對象為參數,當參 ...
  • 英文文檔: ascii(object) As repr(), return a string containing a printable representation of an object, but escape the non-ASCII characters in the string r ...
  • JSTL和EL的使用 使用JSTL前的準備 想要使用JSTL,首先需要給工程導入JSTL的包(JSTL.jar和standard.jar). JSTL標簽庫 在JSTL中分為以下五個標簽 使用不同的標簽的時候需要在JSP中引入不同的庫 核心標簽(Core): 引入標簽庫 <c:set> <c:set ...
  • OSI 七層模型通過七個層次化的結構模型使不同的系統不同的網路之間實現可靠的通訊,因此其最主要的功能就是幫助不同類型的主機實現數據傳輸 。 完成中繼功能的節點通常稱為中繼系統。在OSI七層模型中,處於不同層的中繼系統具有不同的名稱。 一個設備工作在哪一層,關鍵看它工作時利用哪一層的數據頭部信息。網橋 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...