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()方法採用呼叫建構函式,建立類的物件
看下Class.forName()原始碼
//Class.forName(String className) 這是1.8的原始碼 public static Class<?> forName(String className) throws ClassNotFoundException { Class<?> caller = Reflection.getCallerClass(); return forName0(className, true, ClassLoader.getClassLoader(caller), caller); } //注意第二個引數,是指Class被loading後是不是必須被初始化。 不初始化就是不執行static的程式碼即靜態程式碼
然後就是,測試程式碼證明上面的結論是OK的,如下:
package com.lxk.Reflect;
/**
* Created by lxk on 2017/2/21
*/
public class Line {
static {
System.out.println("靜態程式碼塊執行:loading line");
}
}
package com.lxk.Reflect; /** * Created by lxk on 2017/2/21 */ public class Point { static { System.out.println("靜態程式碼塊執行:loading point"); } }
package com.lxk.Reflect;
/**
* Class.forName和classloader的區別
* <p>
* Created by lxk on 2017/2/21
*/
public class ClassloaderAndForNameTest {
public static void main(String[] args) {
String wholeNameLine = "com.lxk.Reflect.Line";
String wholeNamePoint = "com.lxk.Reflect.Point";
System.out.println("下面是測試Classloader的效果");
testClassloader(wholeNameLine, wholeNamePoint);
System.out.println("----------------------------------");
System.out.println("下面是測試Class.forName的效果");
testForName(wholeNameLine, wholeNamePoint);
}
/**
* classloader
*/
private static void testClassloader(String wholeNameLine, String wholeNamePoint) {
Class<?> line;
Class<?> point;
ClassLoader loader = ClassLoader.getSystemClassLoader();
try {
line = loader.loadClass(wholeNameLine);
point = loader.loadClass(wholeNamePoint);
//demo = ClassloaderAndForNameTest.class.getClassLoader().loadClass(wholeNamePoint);//這個也是可以的
System.out.println("line " + line.getName());
System.out.println("point " + point.getName());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* Class.forName
*/
private static void testForName(String wholeNameLine, String wholeNamePoint) {
try {
Class line = Class.forName(wholeNameLine);
Class point = Class.forName(wholeNamePoint);
System.out.println("line " + line.getName());
System.out.println("point " + point.getName());
} catch (Exception e) {
e.printStackTrace();
}
}
}
執行結果如下:
備註:
根據執行結果,可以看到,classloader並沒有執行靜態程式碼塊,如開頭的理論所說。
而下面的Class.forName則是夾在完之後,就裡面執行了靜態程式碼塊,可以看到,2個類,line和point的靜態程式碼塊執行結果是一起的,然後才是各自的列印結果。
也說明上面理論是OK的。
更新於2017/06/20
因為看到有小夥伴有疑問,我就把自己以前的程式碼拿出來再次測試一遍,發現結果仍然是相同的。
但是,因為我的Javabean model又經歷了其他的測試,所以,兩個model內部的程式碼稍有變化,
然後,還真就測試出來了不一樣的地方。
這估計是其他理論所沒有的。具體看下面的程式碼吧。
只是修改了Line的程式碼,添加了幾個靜態的方法和變數。
package com.lxk.reflect;
/**
* Created by lxk on 2017/2/21
*/
public class Line {
static {
System.out.println("靜態程式碼塊執行:loading line");
}
public static String s = getString();
private static String getString() {
System.out.println("給靜態變數賦值的靜態方法執行:loading line");
return "ss";
}
public static void test() {
System.out.println("普通靜態方法執行:loading line");
}
{
System.out.println("要是普通的程式碼塊呢?");
}
public Line() {
System.out.println("構造方法執行");
}
}
可以看到,除了原來的簡單的一個靜態程式碼塊以外,我又添加了構造方法,靜態方法,以及靜態變數,且,靜態變數被一個靜態方法賦值。
然後,看執行結果。
稍有不同。
除了,靜態程式碼塊的執行外,竟然還有一個靜態方法被執行,就是給靜態變數賦值的靜態方法被執行了。
這個估計是以前沒人發現的吧。
所以
上面的結論,就可以進一步的修改啦。
也許,這個執行的也叫,static塊呢。
好吧,
我為什麼會測試這個呢,是因為有一次,有遇到說,類載入的問題,詳情看下面連結。
這個也是值得看一看的。