深入理解JAVA虛擬機-2

JVM參數設置:

  1. -XX:+<option>開啓option選項;例如,-XX:+TraceClassLoading用於追蹤類的加載信息並打印出來;
  2. -XX:-<option>關閉option選項;
  3. -XX:<option>=<value>;將option選擇的值設置爲value;
class MyParent{
    public static final String str="hello world";
 
    static{
         System.out.println(MyParent block);
 
    }
}

加上final修飾,在main方法中打印str,只能打印出來hello world,靜態方法沒有執行,思考why

因爲常量在編譯階段會存入到調用這個方法所在類的常量池中,本質上調用類並沒有直接引用到定義常量的類,因此並不會觸發,定義常量的類的初始化;

助記符:

  1. ldc;將int,float或者String類型的常量從常量池推送到棧頂;
  2. bipush;將單字節(-128-127)的常量從常量池推送到棧頂;
  3. sipush;將短整型(-32768-32767)的常量從常量池推送到棧頂;
  4. iconst_1;將int類型的1推送到棧頂(iconst_1到iconst_5);
  5. anewarray;創建一個引用(類,接口,數組)類型的數組,並將其引用值壓入棧頂;
  6. newarray;創建一個基本類型的數組,將其引用壓入棧頂;
class MyParent{
    public static final String str=UUID.randomUUID().toString();
 
    static{
         System.out.println(MyParent block);
 
    }
}

這種情況下就會運行靜態方法,當一個常量的值並非編譯期可以確定的,那麼其值並不會放到調用類的常量池中,這時在程序運行時,會導致主動使用這個常量所在的類,顯然會初始化這個類。

對於數組實例來說,其類型是在JVM在運行期動態生成的。

接口初始化與類加載:

  1. 當一個接口在初始化時,並不要求其父接口都完成了初始化,只有當真正使用父接口的時候(父接口中的常量)纔會初始化;
    class Singleton{
    
        public static int count1;
        private static Singleton singleton=new Singleton();
    
        private Singleton{
        count1++;
        count2++;
        System.out.println(count1);//1
        System.out.println(count2);//1
        }
    
        public static int count2=0;
    
        public static Singleton getSingleton(){
            return singleton;
        }
    }
    最後打印count1爲1,count2爲0

    當Java虛擬機初始化一個類時,要求它的所有的父類都已經完成了初始化,但是這條規則並不適應用於接口,在初始化一個類時,並不會先初始化它所實現的接口,在初始化一個接口時,並不會先初始化它的父接口。因此,一個父接口並不會因爲它的子接口或者實現類的初始化而初始化,只有當程序使用特定接口的靜態變量時,纔會導致該接口的初始化。

  2. 在父親委託機制中,各個加載器按照父子關係形成了樹形結構,除了根類加載器之外,其餘的類加載器都有且只有一個父類加載器。最頂層是根類加載器,往下依次是擴展類加載器,系統類加載器,用戶自定義類加載器。

  3. 每個類初始化當且只會初始化執行一次。

  4. 調用classLoader類的loadClass方法加載一個類,並不是對類的主動使用,不會導致類的初始化。

類加載器的層次關係:

  1.  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章