Java加载类的方式有两种:
- 通过BootstrapClassLoader 加载,该类是由c++代码实现的,加载lib下面的jar;
- 通过继承ClassLoader来加载类。
- ExtClassLoader, 负责加载java.ext.dirs下面的jar
- AppClassLoader, 负责加载classpath路径下的jar
- 用户自定义的classLoader
java类加载机制采用双亲委托模式,即发现需要加载的类之后先由父类加载,如果父类加载不了再由自己加载。为什么需要双亲委派模型呢?假设没有双亲委派模型,试想一个场景:
黑客自定义一个java.lang.String类,该String类具有系统的String类一样的功能,只是在某个函数稍作修改。比如equals函数,这个函数经常使用,如果在这这个函数中,黑客加入一些“病毒代码”。并且通过自定义类加载器加入到JVM中。此时,如果没有双亲委派模型,那么JVM就可能误以为黑客自定义的java.lang.String类是系统的String类,导致“病毒代码”被执行。
而有了双亲委派模型,黑客自定义的java.lang.String类永远都不会被加载进内存。因为首先是最顶端的类加载器加载系统的java.lang.String类,最终自定义的类加载器无法加载java.lang.String类。
或许你会想,我在自定义的类加载器里面强制加载自定义的java.lang.String类,不去通过调用父加载器不就好了吗?确实,这样是可行。但是,在JVM中,判断一个对象是否是某个类型时,如果该对象的实际类型与待比较的类型的类加载器不同,那么会返回false。
举个简单例子:
ClassLoader1、ClassLoader2都加载java.lang.String类,对应Class1、Class2对象。那么Class1对象不属于ClassLoad2对象加载的java.lang.String类型。
下面我们自己实现一个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即可。
网友评论