一個據說第一次接觸都會做錯的Java面試題和類載入器的介紹
首先什麼話都不說,先把這個很變態的面試題放上來,大家有情趣自己分析一下,然後再執行一下看看結果,據說第
一次遇到這個題目的Java程式設計師都是會做錯的。
[java] view plain copy print?- package com.bird.classLoad;
- publicclass Test1 {
- @SuppressWarnings("static-access")
- publicstaticvoid main(String[] args) {
-
Singleton s = Singleton.getSingleton();
- System.out.println("counter1 = "+ s.counter1);
- System.out.println("counter2 = "+s.counter2);
- }
- }
- class Singleton{
- privatestatic Singleton singleton = new Singleton();
- publicstaticint counter1;
- publicstaticint counter2 = 0;
- public Singleton(){
-
counter1++;
- counter2++;
- }
- publicstatic Singleton getSingleton(){
- return singleton;
- }
- }
如果你沒有分析出來結果是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?- package com.bird.classLoad;
- publicclass Test2 {
- publicstaticvoid main(String[] args) throws Exception {
- Class clazz = Class.forName("java.lang.String");
- System.out.println(clazz.getClassLoader());
- Class clazz2 = Class.forName("com.bird.classLoad.A");
- System.out.println(clazz2.getClassLoader());
- }
- }
- class A{}
- null
- 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
總結一下,這道題真坑爹。