深入了解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()
}
}
……
幫助他人,快樂自己,最後,感謝您的閱讀!
是以如有纰漏或者建議,還請讀者朋友們在評論區不吝指出!
……
個人網站…知識是一種寶貴的資源和财富,益發掘,更益分享…