1. 程式人生 > >一個據說第一次接觸都會做錯的Java面試題和類載入器的介紹

一個據說第一次接觸都會做錯的Java面試題和類載入器的介紹

首先什麼話都不說,先把這個很變態的面試題放上來,大家有情趣自己分析一下,然後再執行一下看看結果,據說第

一次遇到這個題目的Java程式設計師都是會做錯的。

[java] view plain copy  print?
  1. package com.bird.classLoad;  
  2. publicclass Test1 {  
  3.     @SuppressWarnings("static-access")  
  4.     publicstaticvoid main(String[] args) {  
  5.         Singleton s = Singleton.getSingleton();  
  6.         System.out.println("counter1 = "+ s.counter1);  
  7.         System.out.println("counter2 = "+s.counter2);  
  8.     }  
  9. }  
  10. class Singleton{  
  11.     privatestatic Singleton singleton = new Singleton();  
  12.     publicstaticint counter1;  
  13.     publicstaticint counter2 = 0;  
  14.     public Singleton(){  
  15.         counter1++;  
  16.         counter2++;  
  17.     }  
  18.     publicstatic Singleton getSingleton(){  
  19.         return singleton;  
  20.     }  
  21. }  

如果你沒有分析出來結果是1,0那麼請您繼續往下看。

首先介紹一下JVM對一個類的位元組碼class檔案是如果裝載到記憶體中然後被虛擬機器執行的。

1.載入:查詢並載入類的二進位制資料
2.連線
2.1:確保被載入類的正確性
2.2:為類的靜態變數分配記憶體,並將其初始化為預設值
2.3:把類中的符號引用轉換為直接引用
3.初始化:為類的靜態變數賦予正確的初始值

請注意,在連線的時候,為類的靜態變數分配記憶體,初始化為預設值,然後才把類中的符號轉換為指定的引用。

當然了,JVM只有在主動呼叫類的時候才回去載入一個類,主動呼叫一共有六種方式

Java虛擬機器主動使用一個類的六種情況
1.建立類的例項
2.訪問某個類或介面的靜態變數,或者對該靜態變數賦值
3.呼叫類的靜態方法
4.反射
5.初始化一個類的子類
6.Java虛擬機器啟動時被標明為啟動類的類
除了以上六種情況,其他都為被動使用,都不會執行類的初始化

然後對於類載入器

有兩種型別的類載入器
1.根類載入器(使用C++編寫,程式設計師無法在Java程式碼中獲得該類)
2.擴充套件類載入器
3.系統類載入器


使用者自定義類載入器
ClassLoader的子類

比如

[java] view plain copy  print?
  1. package com.bird.classLoad;  
  2. publicclass Test2 {  
  3.     publicstaticvoid main(String[] args) throws Exception {  
  4.         Class clazz = Class.forName("java.lang.String");  
  5.         System.out.println(clazz.getClassLoader());  
  6.         Class clazz2 = Class.forName("com.bird.classLoad.A");  
  7.         System.out.println(clazz2.getClassLoader());  
  8.     }  
  9. }  
  10. class A{}  
執行結果 [java] view plain copy  print?
  1. null
  2. sun.misc.Launcher$AppClassLoader@addbf1

說明String類是JDK自帶的,是使用預設根載入器載入的,它是使用C++編寫的,所以才會返回null,SUN不允許直接

訪問這個根載入器的。

然後對於自己寫的類,當然是通過內部類application載入器載入的,雖然都能使用,但是底層載入方式各不相同。

最後我們來分析程式

首先執行Singleton s = Singleton.getSingleton();

主動呼叫 Singleton,這之後初始化Singleton類,為static變數初始化記憶體空間然後賦予預設值。所以,singleton為

null,counter1為0,counter2為0.

然後賦予引用值。singleton呼叫建構函式,這時候counter1等於1,counter2等於1.

然後順次執行,counter1沒有被賦值,counter2被賦值為0.

這次就返回這個物件了,所以最終結果為,1,0

總結一下,這道題真坑爹。