类加载器类型
Java中有4种类加载器:
- BootstrapClassLoader 最顶层的加载类,主要加载核心类库(c++实现)
- ExtentionClassLoader 扩展的类加载器
- AppClassLoader也称为SystemAppClass 加载当前应用的classpath的所有类。
- 自定义类加载器。
注意:classpath即为java编译的源文件以及一些第三方库所在的文件夹或jar包。详细了解见classpath和jar
加载顺序
双亲(parent)委托机制
遇到加载请求,先把这个请求交给父加载器加载。只有父类加载器找不到class文件,子类才可以加载。所以加载顺序一般是
BootstrapClassLoader -> ExtentionClassLoader -> AppClassLoader -> 自定义类加载器(默认)
父加载器不是父类
加载器类中有一个变量指向父加载器,更继承没啥关系。一般在加载器的构造函数中可以指定父加载器,没指定则默认为AppClassLoader ,指定为null父加载器则为BootstrapClassLoader。
URLClassLoader
可以指定一串路径,然后再些路径下面寻找将要加载的类。这个类加载器如果没有指定的话父类加载器也是AppClassLoader
举个例子。
我们有一个Hello.class文件,位于d:/目录下
public class Hello {
public Hello() {
System.out.println("hello!!!!!!!!!");
}
}
通过URLClassLoader加载这个类
public class ClassLoaderTest {
@Test
public void urlClassLoaderTest() throws Exception {
File file = new File("d:/");
URL url = file.toURI().toURL();
ClassLoader loader=new URLClassLoader(new URL[]{url});
Class<?> clazz = loader.loadClass("Hello");
System.out.println("当前类加载器"+clazz.getClassLoader());
System.out.println("父类加载器"+clazz.getClassLoader().getParent());
clazz.newInstance();
}
}
输出:
如果在当前的classpath也放置一个Hello.class文件,那么就会被APPClassLoader加载,而且加载不到d盘的那个文件。
public class Hello {
public Hello() {
System.out.println("this is another hello class");
}
}
输出:
但是若把URLClassLoader的父类设置为启动类加载器,这样就避免了委派给AppClassLoader时加载了classpath下的同名类文件。
@Test
public void urlClassLoaderTest() throws Exception {
File file = new File("d:/");
URL url = file.toURI().toURL();
ClassLoader loader=new URLClassLoader(new URL[]{url},null);//设为null表示由根加载器加载
Class<?> clazz = loader.loadClass("Hello");
System.out.println("当前类加载器"+clazz.getClassLoader());
System.out.println("父类加载器"+clazz.getClassLoader().getParent());
clazz.newInstance();
}
输出:
与反射创建类的区别
反射创建常用Class.forName,通过类的全限定类名创建,但是只能创建classpath内的类,如果一个类不在classpath中。就只能使用类加载器来创建了。
参考:
https://blog.csdn.net/briblue/article/details/54973413
https://www.liaoxuefeng.com/wiki/1252599548343744/1260466914339296