private static Test test1 = new Test();語句的放置位置

static語句的初始化和類的加載過程共同分析:

第一種情況:

public class Test {


    public static int counter1;
    public static int counter2 = 2;

    private static Test test1 = new Test();

    private Test(){
        System.out.println("count1test1="+ test1.counter1);
        System.out.println("count2test2="+ test1.counter2);
        counter1++;
        counter2++;
        System.out.println("count1test3="+ test1.counter1);
        System.out.println("count2test4="+ test1.counter2);
    }


    public  static Test getInstace(){
        return test1;
    }
}

主函數調用:

public class Mytest {
    public static void main(String[] args) {
        Test test1 = Test.getInstace();
        System.out.println("count1="+ test1.counter1);
        System.out.println("count2="+ test1.counter2);
    }
}

得到的結果:

count1test1=0
count2test2=2
count1test3=1
count2test4=3
count1=1

count2=3

第二種情況:我們將test代碼進行修改:

public class Test {


    private static Test test1 = new Test();

    public static int counter1;
    public static int counter2 = 2;
    
    private Test(){
        System.out.println("count1test1="+ test1.counter1);
        System.out.println("count2test2="+ test1.counter2);
        counter1++;
        counter2++;
        System.out.println("count1test3="+ test1.counter1);
        System.out.println("count2test4="+ test1.counter2);
    }


    public  static Test getInstace(){
        return test1;
    }
}

主函數不變,調用得到的結果:

count1test1=0
count2test2=0
count1test3=1
count2test4=1
count1=1

count2=2


靜態變量的的生命語句,以及靜態的代碼塊都被看做是類的初始化語句(執行類加載的初始化過程),java虛擬機會按照初始化語句在類文件的先後順序來依次執行他們。

比如第一種情況,

執行了count1,給出int類型的默認值是0.

執行了count2,給出賦值的爲2.

執行實例化靜態test1變量,這個時候執行了構造函數,給count1和count2各自進行了加一操作。

第二種情況:

首先執行實例化靜態變量test1操作,執行了構造函數,裏面需要給類加載準備階段的count1和count2默認值都是0,進行加1操作,得到的都是1。實例化語句完成之後。

執行了count1的賦值操作,count1沒有初值,所以值不變仍爲1。

執行了count2的賦值操作,count2有初值是2,所以count2的值變爲了2.


補充類的加載流程

java文件編譯後是字節碼文件,class文件裝載到內存中,這個是有類加載器來完成的。

這個加載過程:加載、連接、驗證、準備、解析、初始化。

加載:將編譯好的字節碼文件在硬盤上加載到內存中,通過類加載器來完成,  具體一點,就是將字節碼文件放到了運行時數據區的方法區內,在java1.8之後方法去合併到了堆區中,然後在堆區創建一 個java.lang.class對象,用來封裝類在方法去內的數據結構。

連接:將讀入到內存中的二進制數據合併到運行環境中去,比如發現類A使用了類B,這個時候我們因爲我們編譯好的文件是獨立   的,加載到內存之後就開始根據調用關係來合併到一起或者相互連接,然後放到了運行時環境中去。這樣形成了類和類之間的關聯關係。

驗證:文件結構,語法,字節碼,二進制等的進行驗證。

很多驗證都是出於安全性的考慮。防止用戶的一些惡意的自己編寫的class文件。

準備:虛擬機爲類的靜態變量分配內存,並設置默認的初始值。比如cnout1和count2在上面,是int類型,在準備階段分配的是int的默認值。

解析:將二進制數據的符號引用替換爲直接引用,比如:類A的AA方法使用了類B的BB方法。在類A的二進制數據中包含了對於類B的BB方法的符號引用(由BB方法的全面和相關描述組成),在解析階段java虛擬機回吧這個符號引用替換爲一直指針,直接指向類B的BB方法在方法區內的位置,這個指針就是直接引用。

解析階段也可以放到初始化之後完成。

初始化:java虛擬機執行類的初始化語句,爲類的靜態變量賦予初始值。就是將默認值進行替換爲初始值。

簡單敘述一下,類的加載過程,後面進行詳細的解析。如有不正確,指指正。


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