類中混合定義靜態字段、靜態塊、實例字段、構造器,且存在前向引用(在聲明前使用後聲明的字段)。new Class() 時,各字段最終值是多少?
public class StaticInitSequence {
// 靜態字段
private static int staticIntVar = 10;
private static int staticComputeIntVar = (int)(Math.random() * 10);
private static String staticStrVar = "Static field init(before)";
private static Object staticRefVar = new Object();
static { // 靜態塊
staticIntVar = 20;
staticStrVar = "Static block init(before)";
staticAfterIntVar = 40; // 前向引用:賦值給後聲明的字段
staticAfterStrVar = "Static block init(after)";
}
private static int staticAfterIntVar = 30; // 靜態塊後聲明
private static String staticAfterStrVar = "Static field init(after)";
// 實例字段
private int fieldIntVar = 100;
private int fieldComputeIntVar = (int)(Math.random() * 100);
private String fieldStrVar = "Instance field init(before)";
public StaticInitSequence() { // 構造器
fieldIntVar = 200;
fieldStrVar = "Constructor field init(before)";
fieldAfterIntVar = 400;
fieldAfterStrVar = "Constructor field init(after)";
}
private int fieldAfterIntVar = 300;
private String fieldAfterStrVar = "Instance field init(after)";
}
--------------Static Fields------------
staticIntVar: 20 // 靜態塊修改
staticComputeIntVar: 7 // 隨機值
staticStrVar: Static block init(before) // 靜態塊修改
staticRefVar: java.lang.Object@... // new Object()
staticAfterIntVar: 30 // 字段初始化覆蓋靜態塊賦值!
staticAfterStrVar: Static field init(after) // 字段初始化覆蓋靜態塊賦值!
-----------------Instance Fields---------
fieldIntVar : 200 // 構造器修改
fieldComputeIntVar : 8 // 隨機值
fieldStrVar : Constructor field init(before) // 構造器修改
fieldAfterIntVar : 400 // 構造器修改
fieldAfterStrVar : Constructor field init(after) // 構造器修改
<clinit> 揭秘Java 編譯器按源代碼順序生成類初始化方法 <clinit>:
關鍵點:staticAfterIntVar 聲明在靜態塊之後,其初始化語句 = 30 會在靜態塊之後執行,覆蓋了塊中的 = 40。
// 靜態塊中賦值
bipush 40
putstatic StaticInitSequence.staticAfterIntVar : int // = 40
// 後聲明字段初始化
bipush 30
putstatic StaticInitSequence.staticAfterIntVar : int // = 30 覆蓋!
<init> 行為不同實例字段初始化語句被編譯器插入到每個構造器的 super() 調用之後、構造器體代碼之前。
且不會重複執行字段初始化(構造器體賦值直接覆蓋)。
故 fieldAfterIntVar 先初始化 300,再被構造器體改為 400,最終 400。
C# 將靜態字段初始化與靜態構造器合併,保證聲明順序執行,但允許前向引用讀取默認值,不會出現「後初始化覆蓋前賦值」的反直覺行為。
<clinit> 線程安全:JVM 保證類初始化原子性,可用於單例模式Java 靜態初始化嚴格遵循源碼順序,這是一種順序耦合。理解字節碼生成規則,避免前向引用賦值,是編寫健壯 Java 代碼的必修課。