[英]java static initialization with inheritance
public class Main {
public static void main(String[] args) {
System.out.println(B.x);
}
}
class A {
public static String x = "x";
}
class B extends A {
static {
System.out.print("Inside B.");
}
}
問題:為什么輸出將是: x
。 但不是: Inside Bx
對Bx
的引用發出以下字節碼:
getstatic #3 <Field int B.x>
Java虛擬機指示anewarray,checkcast,getfield, getstatic ,instanceof,invokedynamic,invokeinterface,invokespecial,invokestatic,invokevirtual,ldc,ldc_w,multianewarray,new,putfield和putstatic對運行時常量池進行符號引用 。 執行任何這些指令都需要解析其符號引用 。
因此JVM應該解析對Bx
的符號引用 。 字段分辨率指定如下 :
要將未解析的符號引用從D解析為類或接口C中的字段,必須首先解析由字段引用給出的對C的符號引用(第5.4.3.1節)。
...
解析字段引用時,字段解析首先嘗試在C及其超類中查找引用的字段:
如果C聲明具有字段引用指定的名稱和描述符的字段,則字段查找成功。 聲明的字段是字段查找的結果。
否則,字段查找以遞歸方式應用於指定類或接口C的直接超接口。
否則,如果C具有超類S,則將字段查找遞歸地應用於S.
否則,字段查找失敗。
換句話說,JVM會將Bx
解析為Ax
。 這就是為什么只需要加載A
類的原因。
因為Bx
實際上是Ax
所以只需要加載A
類。
§12.4JavaSE 7 規范Java語言規范的 “類和接口的初始化”指定:
類的初始化包括執行其
static
初始化程序和類中聲明的靜態字段(類變量)的初始化程序。[...]
對
static
字段(第8.3.1.1節 )的引用僅導致實際聲明它的類或接口的初始化,即使它可能通過子類的名稱,子接口或實現接口的類來引用。
因此,盡管 - 與上述某些答案中的聲明相反 - 必須加載 B
類,為了確定Bx
在A
聲明, B
類未初始化 (即,其static
初始化器實際上並未運行),直到您做一些比B
更具體的事情。
Class B
擴展A
,它有一個public static variable x
,當你調用Bx
時,它正在訪問
如果你期望Inside B.
as out,你必須創建該類的Object。 執行所有靜態代碼塊。 或者將該靜態代碼塊移動到A
類:-)
當JVM加載類時,它會對所有靜態塊進行分組,並按照它們聲明的順序執行它們。
編輯 ( 來源 ):簡短的回答是靜態不是用Java繼承的。 相反,在類中聲明的靜態成員(受“訪問”限制)在派生類的名稱空間中直接可見,除非它們在派生類中被聲明“隱藏”。
因此,如果靜態屬於類只有為什么它會逐漸滲透到派生類? 它不應該只停留在定義它的類中嗎?
它實際上並不需要加載B
,直到它訪問的靜態成員B
直接。 注意這段代碼:
public class TestMain {
public static void main(String[] args) {
System.out.println(B.x);
System.out.println(B.y);
}
static class A {
public static String x = "x";
}
static class B extends A {
public static String y = "y";
static {
System.out.print("Inside B.");
}
}
}
將輸出:
x
Inside B.y
因為它並不需要加載B
,直到東西B
被訪問。
這是關於這個主題的一個很好的鏈接。 從文章“不要忘記,這個代碼將在JVM加載類時執行.JVM將所有這些塊組合成一個靜態塊然后執行。這里有幾點我想提一下:”
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.