天天看點

深入了解Java 反射中 Class.forName 和 ClassLoader 的差別深入了解Java 反射中 Class.forName 和 ClassLoader 的差別?

深入了解Java 反射中 Class.forName 和 ClassLoader 的差別?

  • Class.forName:除了将類的.class檔案加載到jvm中之外,還會預設對類進行初始化,執行類中的靜态代碼塊,以及對靜态變量的指派等操作。
  • ClassLoader:将.class檔案加載到jvm中,預設不會對類進行初始化,隻有在newInstance才會去執行static塊。

1. 解釋

在Java中Class.forName()和ClassLoader都可以對類進行加載。

ClassLoader就是遵循雙親委派模型最終調用啟動類加載器的類加載器,實作的功能是“通過一個類的全限定名來擷取描述此類的二進制位元組流”,擷取到二進制流後放到JVM中。

Class.forName()方法實際上也是調用的CLassLoader來實作的,在Class類中Class.forName()有兩個重載方法,如下:

JDK1.7版本 Class.forName(String className)源碼:

public static Class forName(String s)
        throws ClassNotFoundException
{
    return forName0(s, true, ClassLoader.getCallerClassLoader());
}
           

在Class.forName(String className)方法中會調用的方法是forName0這個方法(是一個native 方法),第二個參數被預設設定為了true,代表是否對加載的類進行初始化,設定為true時會類進行初始化,代表會執行類中的靜态代碼塊,以及對靜态變量的指派等操作。

JDK1.7版本 Class.forName(String name, boolean initialize,ClassLoader loader)源碼:

public static Class forName(String s, boolean flag, ClassLoader classloader)
        throws ClassNotFoundException
{
    if(classloader == null)
    {
        SecurityManager securitymanager = System.getSecurityManager();
        if(securitymanager != null)
        {
            ClassLoader classloader1 = ClassLoader.getCallerClassLoader();
            if(classloader1 != null)
                securitymanager.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION);
        }
    }
    return forName0(s, flag, classloader);
}
           

Class.forName(String name, boolean initialize,ClassLoader loader)方法中的initialize參數用來手動選擇在加載類的時候是否要對類進行初始化,參數為true,則加載的類将會被初始化。

JDK1.7版本 forName0源碼:

private static native Class forName0(String s, boolean flag, ClassLoader classloader)
        throws ClassNotFoundException;
           

2. 舉例

public class Test {
	public static void main(String[] args) {
		try {
			System.out.println("Class.forName開始…");
			Class.forName("com.test.DocTest");
			System.out.println("Class.forName開始!");
				
			System.out.println("ClassLoader開始…");
				ClassLoader.getSystemClassLoader().loadClass("com.test.DocTest");
			System.out.println("ClassLoader結束!");
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
}
	
public class DocTest {
	// 靜态變量
	private static String staticStr;
	// 指派靜态變量的靜态方法
	public static String getStaticStr() {
		System.out.println("執行了DocTest類的 靜态方法");
		return "我是staticStr的值";
	}
	// 靜态代碼塊
	static {
		staticStr = getStaticStr();
		System.out.println("staticStr = "+staticStr);
		System.out.println("執行了DocTest類的 靜态代碼塊");
	}
}

執行結果:
Class.forName開始…
執行了DocTest類的 靜态方法
staticStr = 我是staticStr的值
執行了DocTest類的 靜态代碼塊
Class.forName開始!
ClassLoader開始…
ClassLoader結束!
           

根據運作結果得出Class.forName加載類是将類進了初始化,而ClassLoader的loadClass并沒有對類進行初始化,隻是把類加載到了虛拟機中。

3. 應用場景

在Spring架構中的IOC的實作就是使用的ClassLoader。

而在JDBC時通常是使用Class.forName()方法來加載資料庫連接配接驅動。這是因為在JDBC規範中明确要求Driver(資料庫驅動)類必須向DriverManager注冊自己。

MySQL驅動:

public class Driver extends NonRegisteringDriver implements java.sql.Driver {  
	static {
		try {
			java.sql.DriverManager.registerDriver(new Driver());
		} catch (SQLException E) {
			throw new RuntimeException("Can't register driver!");
		}
	}
	public Driver() throws SQLException {
		// Required for Class.forName().newInstance()
	}
}
           

……

幫助他人,快樂自己,最後,感謝您的閱讀!

是以如有纰漏或者建議,還請讀者朋友們在評論區不吝指出!

……

個人網站…知識是一種寶貴的資源和财富,益發掘,更益分享…