class AAA {
static {
System.out.println("class AAA static block println"); // 并没有打印此句
}
}
public class Main {
public static void main(String[] args) {
System.out.println("hello world!");
}
}
一直以来都以为 static 标识的代码块或者是字段,都是在类加载的时候就被执行或者赋值了,但是这么一看....感觉自己的世界观都要被刷新了。
所以此处是类没有被加载吗?还是说我们一直以来认为的,静态代码块、字段都在类加载的时候被初始化的,这个观点是错误的?
在《深入理解Java虚拟机:JVM高级特性与最佳实践 第2版》中找到一些线索,如下图:
所以,照这么说,是在第一次主动访问该类的时候执行?小弟好生迷惑啊....大家快说说你们的观点
類別初始化和物件初始化。
static包含的程式碼區塊和變數只有在類別初始化的時候才會執行,而初始化的五種條件你也知道啦。
補充說明清楚吧。
首先,你即使放在同一個.java檔案中,編譯後,這還是兩個不同的class文件,不信你看看bin對應的套件下面產生的.class文件。
第二,類別初始化的時候,就會初始化類別的靜態變數和運行靜態程式碼區塊。所以,虛擬機器規定了五種初始化的條件,例如使用了new,getstatic,putstatic指令,main函數所在的類,反射,父類等情況。而,除開這五種情況,是不能觸發類別的初始化的。如你程式碼中所示Main.class中,並沒有任何關於AAA.class的呼叫或父子關係或反射。所以,AAA.class自然不會初始化。
可以看看的另一篇部落格java類別的載入過程
明白了嗎?
-XX:+TraceClassLoading
加上這個會發現沒載AAA
這裡有兩個概念要擼一下:
類載入機制
Java、編譯器、字節碼、JVM的規格與實作。
類別的載入是透過類別載入器(Classloader)完成的,載入的具體策略依賴JVM的具體實現,總的來說可以分兩種:
飢餓式加載,只要被其他類引用到了就加載。
懶惰式加載,當類別被訪問的時候才加載。
Java、編譯器、字節碼、JVM有各自的規範,彼此透過規範協同工作:
編譯按把Java程式碼編譯成規範的字節碼文件,每一個類別(外部類別、內部類別、匿名類別)都會被編譯成一個單獨的字節碼檔案(class檔案),JVM載入類別的時候就是從這些class檔案中一個個的載入。
現在回到你的程式碼:
在Java層,你把AAA、Main兩個類別放在一個檔案中,編譯器編譯後產生兩個class檔:AAA.class、Main.class。
兩個類別在程式碼組織形式上是一起的,但是編譯後卻是獨立的,並且Main並沒有引用AAA,所以無論是哪種類別加載方式都不會觸發對類別AAA的加載,也就不會執行AAA中的靜態程式碼區塊。
真心感謝樓上熱心網友們的解答!
驗證
AAA 類別確實沒有被載入,只有 Main 類別被載入(題幹截圖:初始化條件第四條,主類別被 jvm 自動載入)
結論
類別中 靜態欄位|程式碼區塊 真的是在類別載入的時候被初始化或是執行的!
延伸
怎麼知道類別有沒有被 jvm 所載入?
這也是我一直糾結的問題,一開始以為只要執行了
javac
命令,类就被 jvm 加载了,其实不然,该命令只是将.java
文件转化成 jvm 能读懂的.class
文件而已。那到底怎麼知道類別有沒有被 jvm 所載入?
據 《深入理解Java虛擬機:JVM高級特性與最佳實踐 第2版》 和廣大網友的熱心解答可知,並沒有明確的時機規定了啥時候會被加載!
但是! jvm 明確規定了類別被初始化的時機-就是題幹上截圖部分那四種!而類別的載入是優先於類別初始化的,所以這裡,我們暫且可以認為這幾種情況就是觸發類別載入的條件。
小弟愚昧,總結不妥之處,麻煩大家指正!感謝
把你的Main.java和AAA.java放在同一個資料夾裡,
在main函數裡寫
執行
執行main方法時,只會載入Main類,Main類中並沒有使用到AAA類,並不會去載入AAA類,並不是說把AAA和Main兩個類寫到同一個文件就會同時載入
AAA這個類別既沒有在其他地方new,也沒有對應的去獲取或設定靜態的字段,也沒有去invoke靜態方法。
所以不會自動初始化的。
放在兩個類別裡面了,宣告為public的類別中的mian開始執行,那個類別沒被用到自然不會被載入更別提初始化