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
"java.lang.String"mysql
"javax.swing.JSpinner$DefaultEditor" 内部类名字spring
"java.security.KeyStore$Builder$FileBuilder$1" 内部类FileBuilder中的第一个匿名内部类名字sql
"java.net.URLClassLoader$3$1" 第三个匿名内部类中的第一个匿名内部类(内部类没有名字,就用数字表示)编程
也能够从网络获取。bootstrap
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。 在线程中运行的代码能够经过这个类加载器加载类和资源。 jdbc提供的是接口,米有提供实现。实现是厂商提供的,mysql和oracle不一样的厂商来提供。 JDBC,JNDI,JAXP:这些spi都是利用的是线程上下文类加载器。 TOMCAT为每个应用一个类加载器,使用到了类加载器的隔离,而且违反了双亲委派原则。 */ public static void main(String[] args) { System.out.println(Thread.currentThread().getContextClassLoader());//AppClassLoader,线程上下文加载器, System.out.println(Thread.class.getClassLoader());//null,Thread是根加载器加载的, } }
package com.ssss; public class rrr implements Runnable { private Thread t; public rrr() { t = new Thread(this); t.start(); } public void run() { ClassLoader cl = this.t.getContextClassLoader();//这个线程的上下文类加载器是App。 System.out.println(cl.getClass());//AppClassLoader System.out.println(cl.getParent().getClass());//ExtClassLoader } public static void main(String[] args) { new rrr(); } }
package com.ssss; /* 线程上下文使用模式:获取-------使用-------还原 类A的依赖类也是加载A的加载器加载。 线程上下文就是为了破坏java的委托机制。默认线程上下文加载器是系统app加载器,spi接口的代码中会使用线程上下文加载器 加载spi的实现类。 高层提供统一的接口让底层去实现,高层要加载底层的实现类时候,就呀经过线程上下文加载器来帮助高层加载实现类。 本质是由于高层的加载器和底层加载器不同,而高层加载器不能加载底层的类。 运行期间放置在了线程中,不管当前程序是在启动类加载器范围仍是在扩展加载器范围内,均可以经过Thread.currentThread().getContextClassLoader() 获取应用类加载器。ThreadLocal是拿空间换时间,每一个线程都有一个拷贝。 ClassLoader.getSystemClassLoader()也能够获取app加载器。 */ public class Cat { public static void main(String[] args) { ClassLoader cl = Thread.currentThread().getContextClassLoader();//获取 try { Thread.currentThread().setContextClassLoader(target); incokeMeath();//使用线程上下文的类加载器 }finally { Thread.currentThread().setContextClassLoader(cl);//还原 } System.out.println(ClassLoader.getSystemClassLoader());//AppClassLoader } }