java - Static 標(biāo)識(shí)的字段或者是代碼塊,真的是在類(lèi)加載的時(shí)候初始化的嗎?
問(wèn)題描述
class AAA { static {System.out.println('class AAA static block println'); // 并沒(méi)有打印此句 }}public class Main { public static void main(String[] args) {System.out.println('hello world!'); }}
一直以來(lái)都以為 static 標(biāo)識(shí)的代碼塊或者是字段,都是在類(lèi)加載的時(shí)候就被執(zhí)行或者賦值了,但是這么一看....感覺(jué)自己的世界觀都要被刷新了。
所以此處是類(lèi)沒(méi)有被加載嗎?還是說(shuō)我們一直以來(lái)認(rèn)為的,靜態(tài)代碼塊、字段都在類(lèi)加載的時(shí)候被初始化的,這個(gè)觀點(diǎn)是錯(cuò)誤的?
在《深入理解Java虛擬機(jī):JVM高級(jí)特性與最佳實(shí)踐 第2版》中找到一些線索,如下圖:
所以,照這么說(shuō),是在第一次主動(dòng)訪問(wèn)該類(lèi)的時(shí)候執(zhí)行?小弟好生迷惑啊....大家快說(shuō)說(shuō)你們的觀點(diǎn)
問(wèn)題解答
回答1:類(lèi)初始化和對(duì)象初始化。
static包含的代碼塊和變量只有在類(lèi)初始化的時(shí)候才執(zhí)行,而初始化的五種條件你也知道啦。
補(bǔ)充說(shuō)明清楚吧。首先,你即使放在同一個(gè).java文件中,編譯后,這還是兩個(gè)不同的class文件,不信你看看bin對(duì)應(yīng)的包下面生成的.class文件。第二,類(lèi)初始化的時(shí)候,就會(huì)初始化類(lèi)的靜態(tài)變量和運(yùn)行靜態(tài)代碼塊。所以,虛擬機(jī)規(guī)定了五種初始化的條件,比如使用了new,getstatic,putstatic指令,main函數(shù)所在的類(lèi),反射,父類(lèi)等情況。而,除開(kāi)這五種情況,是不能觸發(fā)類(lèi)的初始化的。正如你代碼中所示Main.class中,并沒(méi)有任何關(guān)于AAA.class的調(diào)用或者父子關(guān)系或者反射。所以,AAA.class自然不會(huì)初始化。
可以看看的另一篇博客java類(lèi)的加載過(guò)程
明白了嗎?
回答2:-XX:+TraceClassLoading加上這個(gè)會(huì)發(fā)現(xiàn)沒(méi)加載AAA
回答3:這里有兩個(gè)概念需要擼一下:
類(lèi)加載機(jī)制
Java、編譯器、字節(jié)碼、JVM的規(guī)范和實(shí)現(xiàn)。
類(lèi)的加載是通過(guò)類(lèi)加載器(Classloader)完成的,加載的具體策略依賴JVM的具體實(shí)現(xiàn),總的來(lái)說(shuō)可以分兩種:
饑餓式加載,只要被其他類(lèi)引用到了就加載。
懶惰式加載,當(dāng)類(lèi)被訪問(wèn)的時(shí)候才加載。
Java、編譯器、字節(jié)碼、JVM有各自的規(guī)范,彼此通過(guò)規(guī)范協(xié)同工作:
編譯按把Java代碼編譯成規(guī)范的字節(jié)碼文件,每一個(gè)類(lèi)(外部類(lèi)、內(nèi)部類(lèi)、匿名類(lèi))都會(huì)被編譯成一個(gè)單獨(dú)的字節(jié)碼文件(class文件),JVM加載類(lèi)的時(shí)候就是從這些class文件中一個(gè)個(gè)的加載。
現(xiàn)在回到你的代碼:
在Java層,你把AAA、Main兩個(gè)類(lèi)放在一個(gè)文件中,編譯器編譯后生成兩個(gè)class文件:AAA.class、Main.class。兩個(gè)類(lèi)在代碼組織形式上是一起的,但是編譯后卻是獨(dú)立的,并且Main并沒(méi)有引用AAA,所以無(wú)論是哪種類(lèi)加載方式都不會(huì)觸發(fā)對(duì)類(lèi)AAA的加載,也就不會(huì)執(zhí)行AAA中的靜態(tài)代碼塊。
回答4:真心感謝樓上熱心網(wǎng)友們的解答!
驗(yàn)證AAA 類(lèi)確實(shí)沒(méi)有被加載,只有 Main 類(lèi)被加載(題干截圖:初始化條件第四條,主類(lèi)被 jvm 自動(dòng)加載)
java -XX:+TraceClassLoading Main結(jié)論
類(lèi)中 靜態(tài)字段|代碼塊 真的是在類(lèi)加載的時(shí)候被初始化或者是執(zhí)行的!
延伸怎么知道類(lèi)有沒(méi)有被 jvm 所加載?這也是我一直糾結(jié)的問(wèn)題,一開(kāi)始以為只要執(zhí)行了 javac 命令,類(lèi)就被 jvm 加載了,其實(shí)不然,該命令只是將 .java 文件轉(zhuǎn)化成 jvm 能讀懂的 .class 文件而已。
那么到底怎么知道類(lèi)有沒(méi)有被 jvm 所加載?據(jù) 《深入理解Java虛擬機(jī):JVM高級(jí)特性與最佳實(shí)踐 第2版》 和廣大網(wǎng)友的熱心解答可知,并沒(méi)有明確的時(shí)機(jī)規(guī)定了啥時(shí)候會(huì)被加載!
但是!jvm 明確規(guī)定了類(lèi)被初始化的時(shí)機(jī)-就是題干上截圖部分那四種!而類(lèi)的加載是優(yōu)先于類(lèi)初始化的,所以這里,我們暫且可以認(rèn)為這幾種情況就是觸發(fā)類(lèi)加載的條件。
小弟愚昧,總結(jié)不妥之處,還麻煩大家指正!感謝
回答5:把你的Main.java和AAA.java放在同一個(gè)文件夾里,
在main函數(shù)里寫(xiě)
Class.forName('AAA');
執(zhí)行
回答6:執(zhí)行main方法時(shí),只會(huì)加載Main類(lèi),Main類(lèi)中并沒(méi)有使用到AAA類(lèi),并不會(huì)去加載AAA類(lèi),并不是說(shuō)把AAA和Main兩個(gè)類(lèi)寫(xiě)到同一個(gè)文件就會(huì)同時(shí)加載
回答7:AAA這個(gè)類(lèi)既沒(méi)有在其他地方new,也沒(méi)有對(duì)應(yīng)的去獲取或者設(shè)置靜態(tài)的字段,也沒(méi)有去invoke靜態(tài)方法。所以不會(huì)自動(dòng)初始化的。
回答8:放在兩個(gè)類(lèi)里面了,聲明為public的類(lèi)中的mian開(kāi)始執(zhí)行,那個(gè)類(lèi)沒(méi)被用到自然不會(huì)被加載更別提初始化
