Java加載類的方式有兩種:
- 通過(guò)BootstrapClassLoader 加載设拟,該類是由c++代碼實(shí)現(xiàn)的久脯,加載lib下面的jar;
- 通過(guò)繼承ClassLoader來(lái)加載類。
- ExtClassLoader, 負(fù)責(zé)加載java.ext.dirs下面的jar
- AppClassLoader, 負(fù)責(zé)加載classpath路徑下的jar
- 用戶自定義的classLoader
java類加載機(jī)制采用雙親委托模式跑慕,即發(fā)現(xiàn)需要加載的類之后先由父類加載摧找,如果父類加載不了再由自己加載。為什么需要雙親委派模型呢芝雪?假設(shè)沒(méi)有雙親委派模型综苔,試想一個(gè)場(chǎng)景:
黑客自定義一個(gè)java.lang.String類位岔,該String類具有系統(tǒng)的String類一樣的功能赃承,只是在某個(gè)函數(shù)稍作修改悴侵。比如equals函數(shù),這個(gè)函數(shù)經(jīng)常使用可免,如果在這這個(gè)函數(shù)中浇借,黑客加入一些“病毒代碼”。并且通過(guò)自定義類加載器加入到JVM中巾遭。此時(shí)闯估,如果沒(méi)有雙親委派模型,那么JVM就可能誤以為黑客自定義的java.lang.String類是系統(tǒng)的String類骑素,導(dǎo)致“病毒代碼”被執(zhí)行刚夺。
而有了雙親委派模型,黑客自定義的java.lang.String類永遠(yuǎn)都不會(huì)被加載進(jìn)內(nèi)存创橄。因?yàn)槭紫仁亲铐敹说念惣虞d器加載系統(tǒng)的java.lang.String類莽红,最終自定義的類加載器無(wú)法加載java.lang.String類。
或許你會(huì)想咖熟,我在自定義的類加載器里面強(qiáng)制加載自定義的java.lang.String類馍管,不去通過(guò)調(diào)用父加載器不就好了嗎?確實(shí)薪韩,這樣是可行捌锭。但是罗捎,在JVM中,判斷一個(gè)對(duì)象是否是某個(gè)類型時(shí)豁状,如果該對(duì)象的實(shí)際類型與待比較的類型的類加載器不同泻红,那么會(huì)返回false。
舉個(gè)簡(jiǎn)單例子:
ClassLoader1谊路、ClassLoader2都加載java.lang.String類缠劝,對(duì)應(yīng)Class1骗灶、Class2對(duì)象。那么Class1對(duì)象不屬于ClassLoad2對(duì)象加載的java.lang.String類型矿卑。
下面我們自己實(shí)現(xiàn)一個(gè)classLoader
import java.io.File;
import java.io.FileInputStream;
import java.lang.reflect.Method;
public class MyClassLoader extends ClassLoader {
private String classPath;
public MyClassLoader(String classPath) {
this.classPath = classPath;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
byte[] data = loadByte(name);
return defineClass(name, data, 0, data.length);
} catch (Exception e) {
e.printStackTrace();
throw new ClassNotFoundException();
}
}
private byte[] loadByte(String name) throws Exception {
System.out.println(classPath + File.separator + name + ".class");
FileInputStream fis = new FileInputStream(classPath + File.separator + name + ".class");
int length = fis.available();
byte[] data = new byte[length];
fis.read(data);
fis.close();
return data;
}
public static void main(String[] args) throws Exception {
ClassLoader classLoader = new MyClassLoader("D:\\t");
Class<?> test = classLoader.loadClass("Test");
Object o = test.newInstance();
Method hello = test.getDeclaredMethod("hello", null);
hello.invoke(o, null);
}
}
只需要覆蓋findClass即可母廷。