jvm原理(7)類加載器與類初始化深度剖析

exampleA
這裏寫圖片描述
打印結果是3 ,並且靜態代碼塊不會執行,原因是x是常量,在編譯期就會放到MyTest8的常量池當中,然後FinalTest和MyTest8就沒有任何關係了,可以通過反編譯的結果看到。
把x的final去掉之後,靜態代碼塊會打印,再去看反編譯結果:

javap -c com.twodragonlake.jvm.classloader.MyTest8
Compiled from "MyTest8.java"
public class com.twodragonlake.jvm.classloader.MyTest8 {
  public com.twodragonlake.jvm.classloader.MyTest8();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: getstatic     #3                  // Field com/twodragonlake/jvm/classloader/FinalTest.x:I
       6: invokevirtual #4                  // Method java/io/PrintStream.println:(I)V
       9: return
}

可以看到main方法的iconst_3變成

3: getstatic     #3                  // Field com/twodragonlake/jvm/classloader/FinalTest.x:I

意味着MyTest8需要引用到FinalTest,兩者之間存在關係的。

exampleB

class Parent{
    static int x = 3;
    static{
        System.out.println("Parent static block");
    }
}

class Child extends  Parent{
    static int b = 4;
    static {
        System.out.println("Child static block");
    }
}

public class MyTest9 {
    static {
        System.out.println("MyTest9 static block");
    }
    public static void main(String[] args) {
        System.out.println(Child.b);
    }
}

打印結果:

Parent static block
Child static block
4

即先加載運行類,然後是Parent和Child
我們可以在jvm啓動參數裏邊加入-XX:TraceClassLoading 看到加載順序:
這裏寫圖片描述

exampleC:

class Parent2{
    static int a = 3;
    static {
        System.out.println("Parent2 static block");
    }
}

class Child2 extends Parent2{
    static int b = 4;
    static {
        System.out.println("Child2 static block");
    }
}

public class MyTest10 {
    static {
        System.out.println("MyTest10 static block");
    }

    public static void main(String[] args) {
        Parent2 parent;
        System.out.println("-----------------");
        parent = new Parent2();
        System.out.println("------------------");
        System.out.println(parent.a);
        System.out.println("------------------");
        System.out.println(Child2.b);

    }
}

打印結果:

MyTest10 static block
-----------------
Parent2 static block
------------------
3
------------------
Child2 static block
4

解析:
運行main方法之前MyTest10初始化,MyTest10的靜態代碼塊執行
Parent2 parent;//此行代碼不會發生任何作用。
System.out.println(“—————–”);
parent = new Parent2();//Parent2主動使用,觸發Parent2的初始化,靜態代碼塊被執行,打印【Parent2 static block】
System.out.println(“——————”);
System.out.println(parent.a);//打印Parent2的靜態變量
System.out.println(“——————”);
System.out.println(Child2.b);//調用Child2的靜態變量 觸發Child2的初始化,從而執行Child2 的靜態代碼塊,先打印Child2 static block,在打印4

exampleD:

class Parent3{
    static int a = 3;
    static {
        System.out.println("Parent3 static block");
    }

    static void doSomething(){
        System.out.println("doSomething...");
    }
}

class Child3 extends  Parent3 {
    static {
        System.out.println("Child3 static block");
    }
}

public class MyTest11 {
    public static void main(String[] args) {
        System.out.println(Child3.a); //a屬於父類,屬於對父類Parent3的主動使用,
        //雖然名字是Child3但是卻不是對Child3的主動使用,導致Parent3的初始化,然後Parent3的靜態代碼塊被執行
        Child3.doSomething();//調用父類Parent3的靜態方法,是對服了的主動使用,觸發父類Parent3的初始化。
    }
}

打印結果:

Parent3 static block
3
doSomething...

結論:
主動使用發生在靜態變量定義在哪個類裏邊,而不是是誰調用了變量,定義變量的類會觸發初始化。

exampleE:

class CL{
    static {
        System.out.println("Class CL");
    }
}

public class MyTest12 {
    public static void main(String[] args) throws Exception {
        ClassLoader classLoader = ClassLoader.getSystemClassLoader();
        Class<?> cl = classLoader.loadClass("com.twodragonlake.jvm.classloader.CL");////不會觸發類的初始化
        System.out.println(cl);
        System.out.println("-------------------------");
        cl = Class.forName("com.twodragonlake.jvm.classloader.CL");////使用了反射,這屬於類初始化時機的反射時機。會觸發類的初始化。
        System.out.println(cl);
    }
}

打印結果:

class com.twodragonlake.jvm.classloader.CL
-------------------------
Class CL
class com.twodragonlake.jvm.classloader.CL

調用classLoader類的loadClass方法加載一個類,並不是對類的主動使用,不會導致類的初始化
這個例子驗證了類的初始化時機的反射時機,具體參考之前的文章:主動使用(七種)

發佈了179 篇原創文章 · 獲贊 41 · 訪問量 18萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章