1. 程式人生 > >Java基礎面試題6-Java反射中Class.forName和classloader的區別

Java基礎面試題6-Java反射中Class.forName和classloader的區別

Java中Class.forName和classloader都可以用來對類進行載入。

Class.forName除了將類的.class檔案載入到jvm中之外,還會對類進行解釋,執行類中的static塊。

而classloader只幹一件事情,就是將.class檔案載入到jvm中,不會執行static中的內容,只有在newInstance才會去執行static塊。

Class.forName(name,initialize,loader)帶引數也可控制是否載入static塊。並且只有呼叫了newInstance()方法採用呼叫建構函式,建立類的物件。

程式碼如下:

複製程式碼
 1 package com.mangosoft.java.reflect;
2 3 /** 4 * Created by zhangshengjian on 2017/3/1. 5 */ 6 public class Line { 7 static { 8 System.out.println("static code executing: loading line..."); 9 } 10 }
複製程式碼複製程式碼
 1 package com.mangosoft.java.reflect;
 2 
 3 /**
 4  * Created by zhangshengjian on 2017/3/1.
 5  */
 6 public class Point {
7 static { 8 System.out.println("static code executing: loading point..."); 9 } 10 }
複製程式碼複製程式碼
 1 package com.mangosoft.java.reflect;
 2 
 3 /**
 4  * classloader和Class.forName的區別
 5  *
 6  * Created by zhangshengjian on 2017/3/1.
 7  */
 8 public class ClassLoaderAndForNameTest {
 9 
10     public
static void main(String[] args) { 11 String wholeNameLine = "com.mangosoft.java.reflect.Line"; 12 String wholeNamePoint = "com.mangosoft.java.reflect.Point"; 13 14 System.out.println("classloader testing..."); 15 testClassLoader(wholeNameLine, wholeNamePoint); 16 System.out.println("---------------------------------------"); 17 System.out.println("Class.forName testing..."); 18 testForName(wholeNameLine, wholeNamePoint); 19 } 20 21 //classloader 22 public static void testClassLoader(String wholeNameLine, String wholeNamePoint) { 23 ClassLoader loader = ClassLoader.getSystemClassLoader(); 24 Class<?> line; 25 Class<?> point; 26 try { 27 line = loader.loadClass(wholeNameLine); 28 point = loader.loadClass(wholeNamePoint); 29 System.out.println("line " + line.getName()); 30 System.out.println("point " + point.getName()); 31 } catch (ClassNotFoundException e){ 32 e.printStackTrace(); 33 } 35 } 36 37 //Class.forName 38 public static void testForName(String wholeNameLine, String wholeNamePoint) { 39 try { 40 Class line = Class.forName(wholeNameLine); 41 Class point = Class.forName(wholeNamePoint); 42 System.out.println("line " + line.getName()); 43 System.out.println("point " + point.getName()); 44 } catch (ClassNotFoundException e) { 45 e.printStackTrace(); 46 } 47 } 48 }
複製程式碼

執行結果如下:

不要讓任何事情成為你不去學習的理由!

轉載原處:https://www.cnblogs.com/mangosoft/p/6485790.html

在理解這兩種反射機制之前,需要弄清楚java類的載入機制.

裝載:通過類的全限定名獲取二進位制位元組流(二進位制的class檔案),將二進位制位元組流轉換成方法區中的執行時資料結構,在記憶體中生成Java.lang.class物件。這個時候該型別沒有被分配記憶體,設定預設值,也沒有初始化。

連結:執行下面的校驗、準備和解析步驟,其中解析步驟是可以選擇的;

  校驗:檢查匯入類或介面的二進位制資料的正確性;(檔案格式驗證,元資料驗證,位元組碼驗證,符號引用驗證)

  準備:給類的靜態變數分配並初始化儲存空間;

  解析:將常量池中的符號引用轉成直接引用;

初始化:啟用類的靜態變數的初始化Java程式碼和靜態Java程式碼塊,並初始化程式設計師設定的變數值。

Class.forName有兩個過載方法:

複製程式碼
 public static Class<?> forName(String name, boolean initialize,
                   ClassLoader loader)
        throws ClassNotFoundException


 public static Class<?> forName(String className) 
                throws ClassNotFoundException
複製程式碼

解釋:

name:類的全限定名,如:com.org.prj
initialize:如果為true,則會在返回Class物件之前,對該型別做連線,校驗,初始化操作。(如:執行static塊中的程式碼),initialize預設需要初始化。
loader:用自定義的類載入器來請求這個型別;當然,你也可以傳入null,用bootstrap載入器

由於Class.forName預設是需要初始化,一旦初始化,就會觸發目標物件的 static塊程式碼執行,static引數也也會被再次初始化。

我們在來看ClassLoader.loadClass也有兩個兩個過載方法:

protected synchronized Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException

public Class<?> loadClass(String name) throws ClassNotFoundException 

解釋:

name:類的全限定名,如:com.org.prj

resolve:表示是否需要連線該型別。 僅僅是連線(這裡麵包括校驗class檔案,準備分配記憶體,型別常量池的替換),並不會初始化該型別。

resolve預設是不連結,不進行連結意味著不進行包括初始化等一些列步驟,那麼靜態塊和靜態物件就不會得到執行。

總結:

1.Class.forName返回的Class物件可以決定是否初始化。而ClassLoader.loadClass返回的型別絕對不會初始化,最多隻會做連線操作。 
2.Class.forName可以決定由哪個classLoader來請求這個型別。而ClassLoader.loadClass是用當前的classLoader去請求。