//启动类BootStrap,负责JAVA_HOME/lib下面的核心类库或-Xbootclasspath
System.getProperty("sun.boot.class.path");
//扩展类 ExtClassLoader,负责JAVA_HOME/lib/ext
System.getProperty("java.ext.dirs");
//系统类 AppClassLoader,它负责将用户类路径(java -classpath或-Djava.class.path变量所指的目录,即当前类所在路径及其引用的第三方类库的路径
System.getProperty("java.class.path");
/**
* 双亲委派机制,当一个类需要加载时,类加载器是将它委托给父亲加载,如果父亲不加载,那么自己才会加载,越是基础
* 的类,将会有启动类加载
*
* 系统类和扩展类的公共父类java.net.URLClassLoader和java.security.SecureClassLoader
*
* ClassLoader抽象类
* loadClass方法,引发 ClassNotFoundException
* findClass方法,引发 ClassNotFoundException
* defineClass方法,引发 NoClassDefFoundError ,定义一个类的字节码文件
* findLoadedClass方法,是否已经加载了该类
*/
/**
* 系统类的父类是扩展类,扩展类的父类是启动类,但是启动类无法获取
* 在类加载器的代理委派模型的时候,提到过类加载器会首先代理给其它类加载器来尝试加载某个类,
* 这就意味着真正完成类的加载工作的类加载器和启动这个加载过程的类加载器,有可能不是同一个。
* 真正完成类的加载工作是通过调用defineClass来实现的;
* 而启动类的加载过程是通过调用loadClass来实现的。
* 前者称为一个类的定义加载器(defining loader),后者称为初始加载器(initiating loader)。
* 在Java虚拟机判断两个类是否相同的时候,使用的是类的定义加载器。
* initialize表明加载的时候是否进行初始化
* public static Class<?> forName(String name, boolean initialize, ClassLoader loader) throws ClassNotFoundException
*/
System.out.println(ClassLoader.getSystemClassLoader());
System.out.println(ClassLoader.getSystemClassLoader().getParent());
System.out.println(ClassLoader.getSystemClassLoader().getParent().getParent());
/**
* 如果需要创建自己的类加载器,那么最好覆盖findClass方法,不用覆盖loadClass
*/
Person person=new Person("lin");
System.out.println(person);
Class personOfClassLoader=Class.forName("com.Person");
Person p1= (Person) personOfClassLoader.newInstance();
p1.setName(person.getName());
System.out.println(person==p1); //false
System.out.println(personOfClassLoader.getClassLoader());
Class c=ClassLoader.getSystemClassLoader().loadClass("com.Person");
Constructor constructor= c.getConstructor(String.class);
System.out.println(constructor);
Person p2= (Person) constructor.newInstance(person.getName());
System.out.println(p2==person); //false
/**
* SPI服务,为某个接口寻找服务实现的机制,约定者定义接口,不同的厂商提供实现类,定义在jar包的META-INF/services/目录,目录里同时创建一个以服务接口命名的文件,该文件里就是实现该服务接口的具体实现类。
* 同时Java定义了java.util.ServiceLoader工具类,加载接口的实现类,开发只需要使用接口调用即可调用实现类的方法
*
* SPI的接口是Java核心库的一部分,是由启动类加载器来加载的;
* 而SPI的实现类是由系统类加载器来加载的。
*
* 启动类加载器是无法找到 SPI 的实现类的(因为它只加载 Java 的核心库),按照双亲委派模型,启动类加载器无法委派系统类加载器去加载类。
* 也就是说,类加载器的双亲委派模式无法解决这个问题。
** 线程上下文类加载器,线程上下文类加载器破坏了“双亲委派模型”,可以在执行线程中抛弃双亲委派加载链模式,使程序可以逆向使用类加载器。
*
* Java.lang.Thread中的方法 getContextClassLoader()和 setContextClassLoader(ClassLoader cl)用来获取和设置线程的上下文类加载器。
* 如果没有通过 setContextClassLoader(ClassLoader cl)方法进行设置的话,线程将继承其父线程的上下文类加载器。
* Java 应用运行的初始线程的上下文类加载器是系统类加载器
*
* 自己加载不了的类加载到线程上下文类加载器中(通过Thread.currentThread()获取),
* 而线程上下文类加载器默认是使用系统类加载器AppClassLoader。
*/