public class TT { public static void main(String[] args) throws Exception { String[] ss = new String[3]; //String是boot加载器加载的。 System.out.println(ss.getClass().getClassLoader());//null, int[] tr = new int[3]; //如果元素是原生类型是没有类加载器的。 System.out.println(tr.getClass().getClassLoader());//null。这里的null是因为原生类型,上面的null是因为boot加载器。 TT[] t = new TT[3]; //数组对象的getClassLoader()返回值跟数组元素的类加载器是一样的 System.out.println(t.getClass().getClassLoader());//AppClassLoader@73d16e93 Integer[] gg = new Integer[3]; //如果元素是原生类型是没有类加载器的。 System.out.println(gg.getClass().getClassLoader());//null } }
ClassLoader抽象类,用于加载类,不是加载对象。给定类的二进制名字,
"java.lang.String"
"javax.swing.JSpinner$DefaultEditor" 内部类名字
"java.security.KeyStore$Builder$FileBuilder$1" 内部类FileBuilder中的第一个匿名内部类名字
"java.net.URLClassLoader$3$1" 第三个匿名内部类中的第一个匿名内部类(内部类没有名字,就用数字表示)
也可以从网络获取。
Class类里面有private final ClassLoader classLoader;
数组类的Class对象不是类加载器创建的,是由虚拟机运行时自动创建的。只有数组的特殊的。
数组对象的getClassLoader()返回值跟数组元素的类加载器是一样的; 如果元素是原生类型是没有类加载器的。
默认是双亲委托方式,这是为了安全起见,如果要改变这种方式,就要自己实现类加载器。


每个ClassLoader都有一个private final ClassLoader parent;就是他的父类加载器,所以加载器的父类加载器是包含关系。
不同的类加载器都是加载不同环境变量指定目录下的类。

public class T { public static void main(String[] args) { System.out.println(System.getProperty("sun.boot.class.path"));//bootStrap启动.根 类加载器加载的路径: //C:\Program Files\Java\jdk1.8.0_181\jre\lib\endorsed\rt_debug.jar; //C:\Program Files\Java\jdk1.8.0_181\jre\lib\resources.jar; //C:\Program Files\Java\jdk1.8.0_181\jre\lib\rt.jar; //C:\Program Files\Java\jdk1.8.0_181\jre\lib\jsse.jar; //C:\Program Files\Java\jdk1.8.0_181\jre\lib\jce.jar; //C:\Program Files\Java\jdk1.8.0_181\jre\lib\charsets.jar; //C:\Program Files\Java\jdk1.8.0_181\jre\lib\jfr.jar; //C:\Program Files\Java\jre1.8.0_181\lib\endorsed\rt_debug.jar System.out.println(System.getProperty("java.ext.dirs"));//扩展类加载器加载的路径: //C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext; //C:\Windows\Sun\Java\lib\ext System.out.println(System.getProperty("java.class.path"));//app.应用.系统 类加载器加载的路径: //H:\2019326spring\蚂蚁课堂\0005-(每特教育&每特学院&蚂蚁课堂)-3期-并发编程专题-线程池原理分析\0005- //(每特教育&每特学院&蚂蚁课堂)-3期-并发编程专题-线程池原理分析\上课代码\thread_day_day06_test\bin } //手动把class文件放在bootstarp类加载器的目录下,那么这个class文件就由根加载器加载。 }



Loader1和Loader2是2个不同的类加载器对象,虽然是同一个类加载器类。Class1和class2是同一个包名和类名的类的Class对象。Loader1和Loader2构成了2个命名空间。
同一个命名空间加载同一个包和类名的类,是失败的,因为他说已经加载了这个类。
2个不同的类加载器loader1和loader2可以加载同一个包名和类名的类,因此class1和class2虽然包名和类名一样,但是再虚拟机中是可以共存的。
同一个包名和类名的类对象,由于是不同的类加载加载,所以在不同的命名空间,是不可见的,不能使用,更不能赋值和转换。Object1不能转换成object2。

类加载器找的是class对象。
子加载器可以看到父加载器的,父加载器看不到子加载器的。
一个java类是由类的完全限定名和加载这个类的定义类加载器共同决定的。
数组不是类加载器加载的,是jvm运行时候创建的。
扩展类加载器和应用类加载器是由启动类加载器加载的,启动类加载器是由jvm加载的。
Ideal自带的反编译器反编译的。
Jdbc是一个标准,oracle厂商会根据这个标准去实现,jdbc是一个标准,原生的在jdk中,Connection和Statement接口在rt.jar里面,由bootStrap加载器加载。但是Connection和Statement的实现是由厂商实现的,那么就是将厂商提供的jar包放在应用的类路径下,所以厂商的实现就不能由bootstrap加载,因为bootstarp不会去扫描应用的类路径,只能由系统加载器appclassloader加载。
Connection接口是启动类加载器加载的,Connection的实现启动类加载器加载不了,只能由系统加载器加载。Connection接口看不到他的实现,这是双亲委托出现的问题。
在jdbc,jndi,xml解析都会出现,就是说在SPI场合都有这个问题。
SPI:Service Provider Inteferce。jdbc,jndi都是SPI。Jdk服务提供者仅仅提供一些标准和接口,具体的实现由厂商来实现的。父加载器的类看不到子加载器的,子加载器的类可以看到父加载器的。
父加载器可以使用当前线程Thread.currentThread().getContextClassLoader()所指定的类加载器加载的类。这就改变了父加载器不能使用子加载器的类的情况。就改变了双亲委托模型。
线程上下文类加载器就是当前线程的类加载器。
SPI:通过给当前线程设置上下文类加载器,就可以由线程的上下文类加载器来加载接口的实现类。
框架开发和组件的开发有用到线程上下文类加载器。
package com.ssss; public class T { /*当前类加载器: 每个类都会使用加载自身的类加载器,去加载所引用的类。 线程上下文类加载器从jdk1.2引入。 Thread.currentThread().setContextClassLoader() Thread.currentThread().getContextClassLoader()分别用于设置和获取上下文类加载器。 默认下,线程继承父线程的上下文类加载器,启动应用线程的上下文加载器是系统加载器。所以默认是AppClassLoder。 在线程中运行的代码可以通过这个类加载器加载类和资源。 */ public static void main(String[] args) { System.out.println(Thread.currentThread().getContextClassLoader());//AppClassLoader,线程上下文加载器, System.out.println(Thread.class.getClassLoader());//null,Thread是根加载器加载的, } }
原文:https://www.cnblogs.com/yaowen/p/10959256.html