参考 <<深入理解java虚拟机 JVM高级特性与最佳实践>>
什么是类加载器?
是一组代码,通过类的全限定名来获取描述此类的二进制字节流信息。让应用程序来决定如何去获取所需要的类。
应用场景:类层次划分,OSGI, 热部署, 代码加密
用于类加载流程中的加载阶段。
对于任意一个类,都需要由它的类加载器和这个类本身一同确立其在java虚拟机中的唯一性
如下代码:
public static void main(String[] args) {
ClassLoader myclassLoader = new ClassLoader() {
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
try {
String fileName = name.substring(name.lastIndexOf(".")+1)+".class";
InputStream in = getClass().getResourceAsStream(fileName);
if(in == null){
return super.loadClass(name);
}
byte[] b = new byte[in.available()];
in.read(b);
return defineClass(name, b, 0, b.length);
} catch (IOException e) {
throw new ClassNotFoundException(name);
}
}
};
try {
Object o = myclassLoader.loadClass("com.tiany.test.ClassloaderTest").newInstance();
System.out.println("ClassloaderTest -->"+o.getClass());
System.out.println(o instanceof com.tiany.test.ClassloaderTest);
Object object = myclassLoader.loadClass("com.tiany.lock.TwinsLock").newInstance();
System.out.println("TwinsLock-->"+object.getClass());
System.out.println(object instanceof com.tiany.lock.TwinsLock);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
输出结果:
ClassloaderTest -->class com.tiany.test.ClassloaderTest
false
TwinsLock-->class com.tiany.lock.TwinsLock
true
问题1
只有当前main方法所在的类用自定义的加载器加载出来的才是不同的,但其它的类加载却是返回true的。
无论我调整自定义加载器myclassLoader的位置(放置到另一个类中),结果都与上面结果一样。
jvm中类加载器的种类
- BootStrapLoader 启动类加载器 会加载一些JVM自身运行所需的class
- ExtClassLoader 扩展类加载器 会加载指定目录下一些特殊的class
AppClassLoader 应用程序加载类 会加载classpath路径下的class,以及main函数所在的类的class文件。
加载的方式:
双亲委派模式
如果一个类加载器受到了类加载的请求,首先不会自己加载这个类,每一个层次的类加载器都是如此,所有的加载器最终都会传送到最顶层的BootStapLoader加载器中,只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。
回到上面代码中的问题1:
假设:
主线程 main 启动首先会对ClassLoaderTest进行加载,用的是appClassLoader.
定义自己的加载器后myClassLoader后,调用父类加载器无法完成加载(没有在父类加载器范围内找到这个类),只能通过自定义的加载器进行加载,所以instanceof 判断是两个不同的类
而其它的类都是jvm启动后使用时才进行加载的。原本方法区可能没有这个类,自定义的加载也能调用父类的加载成功,所以它的类型是一致的。
测试:
在loadClass方法中加上
Class clazz = super.findLoadedClass(name);
if(clazz == null){
System.out.println(name+"还未被加载");
}else {
System.out.println(name + "已经被加载过了");
}
结果:
com.tiany.test.ClassloaderTest$1还未被加载
ClassloaderTest -->class com.tiany.test.ClassloaderTest
false
com.tiany.lock.TwinsLock还未被加载
TwinsLock-->class com.tiany.lock.TwinsLock
true
根据当前的名称加载的居然是ClassloaderTest的内部类,而且不是ClassloaderTest本身