看起來很懵的java內存加載面試題


源代碼如下,求結果

public class MemoryAnalyse {
    public static int k = 0;
    public static MemoryAnalyse t1 = new MemoryAnalyse("t1");
    public static MemoryAnalyse t2 = new MemoryAnalyse("t2");
    public static int i = print("i");
    public static int j = print("j");
    public static int n = 99;
    
    {
        print("constructor code");
    }
    
    static {
        print("static code");
    }

    public static int print(String s) {
        System.out.println("i=" + i + "   " + s + "  k=" + k + "  n=" + n
                + "   j=" + j);
        ++i;
        ++k;
        ++n;
        return i;
    }

    public MemoryAnalyse(String string) {
        print(string);
    }

    public static void main(String[] args) throws ClassNotFoundException {
        MemoryAnalyse d = new MemoryAnalyse("T");
    }
}

源碼下載

然而結果是這個

i=0   constructor code  k=0  n=0   j=0
i=1   t1  k=1  n=1   j=0
i=2   constructor code  k=2  n=2   j=0
i=3   t2  k=3  n=3   j=0
i=4   i  k=4  n=4   j=0
i=5   j  k=5  n=5   j=0
i=6   static code  k=6  n=99   j=6
i=7   constructor code  k=7  n=100   j=6
i=8   T  k=8  n=101   j=6

有沒有很驚訝,結果竟然這么復雜.好,下面我們分析一下,在分析之前,先普及下不怎么用的基礎知識

代碼塊和靜態代碼塊何時運行問題:

  代碼塊在創建對象時運行
  靜態代碼塊在類加載時運行

大家都知道static是屬於類的並非對象,也就是說static修飾的東西都會在class加載到方法區時就存在在那里.所以方法區中類加載時內存過程如下

1.當類剛加載時會全部加載到方法區時,此時所有變量全部未實例化.

2.實例化參數t1

此時因為代碼塊在創建對象時執行,且在構造函數之前執行,所以先執行代碼塊

{
print("constructor code");
}

  因為此時所有的變量都為默認值,所以執行后打印結果為

i=0   constructor code  k=0  n=0   j=0

此時i,n,k的值都已經自加一,值為1

然后實例化調用構造函數

public MemoryAnalyse(String string) {   //這里string為t1
        print(string);
}

  構造函數調用結果如下

  i=1   t1  k=1  n=1   j=0
  此時i,n,k的值都已經自加一,值為2

3.實例化參數t2

和第一步一樣在構造函數之前執行代碼塊

{
print("constructor code"); }

i=2   constructor code  k=2  n=2   j=0

此時i,n,k的值都已經自加一,值為3

然后實例化調用構造函數

public MemoryAnalyse(String string) {   //這里string為t2
        print(string);
}

  構造函數調用結果如下

 

i=3   t2  k=3  n=3   j=0

此時i,n,k的值都已經自加一,值為4

4.初始化參數i

  這里直接調用print("i")函數,得到結果為

  i=4   i  k=4  n=4   j=0

  此時i,k,j值為5,注意i的值不是通過自加一變成5的,而是通過函數的返回值賦給i的

  5.初始化參數j

  

  這里和上一步一樣,執行print("j"),然后把函數的返回值賦給j,打印結果為

  i=5   j  k=5  n=5   j=0

  此時j的值已經為6

到這里類加載的內部參數變化就完成了,我們可以用加載類的方式調用一下

    public static void main(String[] args) throws ClassNotFoundException {
        //MemoryAnalyse d = new MemoryAnalyse("T");
        Class.forName("MemoryAnalyse");
    }

Class.forName(類名字符串)是手動加載類到方法區,得到結果為

i=0   constructor code  k=0  n=0   j=0
i=1   t1  k=1  n=1   j=0
i=2   constructor code  k=2  n=2   j=0
i=3   t2  k=3  n=3   j=0
i=4   i  k=4  n=4   j=0
i=5   j  k=5  n=5   j=0
i=6   static code  k=6  n=99   j=6  //這行的出現是因為static代碼段在類加載時執行.n=99是因為類加載了,n的初值為99把之前的值覆蓋掉了

然后改為我們之前的demo

public static void main(String[] args) throws ClassNotFoundException {
        MemoryAnalyse d = new MemoryAnalyse("T");
        //Class.forName("MemoryAnalyse");
    }

執行結果為

i=0   constructor code  k=0  n=0   j=0    //t1代碼塊執行
i=1   t1  k=1  n=1   j=0  //t1構造函數執行
i=2   constructor code  k=2  n=2   j=0   //t2代碼塊執行
i=3   t2  k=3  n=3   j=0  //t2構造函數執行
i=4   i  k=4  n=4   j=0
i=5   j  k=5  n=5   j=0
i=6   static code  k=6  n=99   j=6  //靜態代碼塊執行
i=7   constructor code  k=7  n=100   j=6    //T代碼塊執行
i=8   T  k=8  n=101   j=6     //T構造函數執行

最后兩行的出現就很簡單了,一個是代碼塊的,一個是構造函數的

這么一分析是不是簡單了很多


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM