搜尋

首頁  >  問答  >  主體

java - Static 标识的字段或者是代码块,真的是在类加载的时候初始化的吗?

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版》中找到一些线索,如下图:

所以,照这么说,是在第一次主动访问该类的时候执行?小弟好生迷惑啊....大家快说说你们的观点

PHPzPHPz2807 天前721

全部回覆(8)我來回復

  • ringa_lee

    ringa_lee2017-04-18 10:54:20

    類別初始化和物件初始化。

    static包含的程式碼區塊和變數只有在類別初始化的時候才會執行,而初始化的五種條件你也知道啦。

    補充說明清楚吧。
    首先,你即使放在同一個.java檔案中,編譯後,這還是兩個不同的class文件,不信你看看bin對應的套件下面產生的.class文件。
    第二,類別初始化的時候,就會初始化類別的靜態變數和運行靜態程式碼區塊。所以,虛擬機器規定了五種初始化的條件,例如使用了new,getstatic,putstatic指令,main函數所在的類,反射,父類等情況。而,除開這五種情況,是不能觸發類別的初始化的。如你程式碼中所示Main.class中,並沒有任何關於AAA.class的呼叫或父子關係或反射。所以,AAA.class自然不會初始化。

    可以看看的另一篇部落格java類別的載入過程

    明白了嗎?

    回覆
    0
  • 阿神

    阿神2017-04-18 10:54:20

    -XX:+TraceClassLoading
    加上這個會發現沒載AAA

    回覆
    0
  • PHP中文网

    PHP中文网2017-04-18 10:54:20

    這裡有兩個概念要擼一下:

    1. 類載入機制

    2. Java、編譯器、字節碼、JVM的規格與實作。

    類別的載入是透過類別載入器(Classloader)完成的,載入的具體策略依賴JVM的具體實現,總的來說可以分兩種:

    1. 飢餓式加載,只要被其他類引用到了就加載。

    2. 懶惰式加載,當類別被訪問的時候才加載。

    Java、編譯器、字節碼、JVM有各自的規範,彼此透過規範協同工作:

    編譯按把Java程式碼編譯成規範的字節碼文件,每一個類別(外部類別、內部類別、匿名類別)都會被編譯成一個單獨的字節碼檔案(class檔案),JVM載入類別的時候就是從這些class檔案中一個個的載入。

    現在回到你的程式碼:

    在Java層,你把AAA、Main兩個類別放在一個檔案中,編譯器編譯後產生兩個class檔:AAA.class、Main.class。
    兩個類別在程式碼組織形式上是一起的,但是編譯後卻是獨立的,並且Main並沒有引用AAA,所以無論是哪種類別加載方式都不會觸發對類別AAA的加載,也就不會執行AAA中的靜態程式碼區塊。

    回覆
    0
  • ringa_lee

    ringa_lee2017-04-18 10:54:20

    真心感謝樓上熱心網友們的解答!

    驗證

    AAA 類別確實沒有被載入,只有 Main 類別被載入(題幹截圖:初始化條件第四條,主類別被 jvm 自動載入)

    java -XX:+TraceClassLoading Main

    結論

    類別中 靜態欄位|程式碼區塊 真的是在類別載入的時候被初始化或是執行的!

    延伸

    怎麼知道類別有沒有被 jvm 所載入?

    這也是我一直糾結的問題,一開始以為只要執行了 javac 命令,类就被 jvm 加载了,其实不然,该命令只是将 .java 文件转化成 jvm 能读懂的 .class 文件而已。

    那到底怎麼知道類別有沒有被 jvm 所載入?
    據 《深入理解Java虛擬機:JVM高級特性與最佳實踐 第2版》 和廣大網友的熱心解答可知,並沒有明確的時機規定了啥時候會被加載!

    但是! jvm 明確規定了類別被初始化的時機-就是題幹上截圖部分那四種!而類別的載入是優先於類別初始化的,所以這裡,我們暫且可以認為這幾種情況就是觸發類別載入的條件。

    小弟愚昧,總結不妥之處,麻煩大家指正!感謝

    回覆
    0
  • PHPz

    PHPz2017-04-18 10:54:20

    把你的Main.java和AAA.java放在同一個資料夾裡,

    在main函數裡寫

    Class.forName("AAA");
    

    執行

    回覆
    0
  • 大家讲道理

    大家讲道理2017-04-18 10:54:20

    執行main方法時,只會載入Main類,Main類中並沒有使用到AAA類,並不會去載入AAA類,並不是說把AAA和Main兩個類寫到同一個文件就會同時載入

    回覆
    0
  • 大家讲道理

    大家讲道理2017-04-18 10:54:20

    AAA這個類別既沒有在其他地方new,也沒有對應的去獲取或設定靜態的字段,也沒有去invoke靜態方法。
    所以不會自動初始化的。

    回覆
    0
  • 迷茫

    迷茫2017-04-18 10:54:20

    放在兩個類別裡面了,宣告為public的類別中的mian開始執行,那個類別沒被用到自然不會被載入更別提初始化

    回覆
    0
  • 取消回覆