类加载器

今天学习工厂模式解耦操作时,误用了properties类的类加载器去获取配置配置文件,报了空指针异常,查class类的源码发现如下内容:

/**  Returns the class loader for the class.  Some implementations may use 
 * null to represent the bootstrap class loader. This method will return
 * null in such implementations if this class was loaded by the bootstrap class loader.

properties类的类加载器是bootstrap class loader,所以该方法返回的是null。
除了properties,我还用了object类和自定义的一些类来获取类加载器,发现系统类使用时,都抛出了空指针异常,而自定义的类在获取加载器时可以正常操作

            Object o = new Object();
            ClassLoader classLoader1 = o.getClass().getClassLoader();
            IAccountDao iAccountDao = new IAccountDaoImpl();
            ClassLoader classLoader2 = iAccountDao.getClass().getClassLoader();
            System.out.println(classLoader2);
            System.out.println(classLoader1);
//运行结果是
//sun.misc.Launcher$AppClassLoader@135fbaa4
//null

通过getParent()方法可以看到分层情况:

IAccountDao iAccountDao = new IAccountDaoImpl();
            ClassLoader classLoader2 = iAccountDao.getClass().getClassLoader();
            System.out.println(classLoader2);
            System.out.println(classLoader2.getParent());
            System.out.println(classLoader2.getParent().getParent());
//结果是
//sun.misc.Launcher$AppClassLoader@135fbaa4
//sun.misc.Launcher$ExtClassLoader@2503dbd3
//null

查询类加载器的种类时发现,自定义的这些类是


类加载器_第1张图片
image.png

类加载器按照层次,从顶层到底层,分为以下四种:

1.启动类加载器:这个类加载器负责放在\lib目录中的,或者被-Xbootclasspath参数所指定的路径中的,并且是虚拟机识别的类库加载到内存中。启动类加载器无法被Java程序直接引用。
2.扩展类加载器:这个类加载器由sun.misc.Launcher$ExtClassLoader实现。它负责\lib\ext目录中的,或者被java.ext.dirs系统变量所指定的路径中的所有类库。用户可以直接使用。
3.应用程序类加载器:这个类由sun.misc.Launcher$AppClassLoader实现。是ClassLoader中getSystemClassLoader()方法的返回值。它负责用户路径(ClassPath)所指定的类库。用户可以直接使用。如果用户没有自己定义类加载器,默认使用这个。
4.自定义加载器:用户自己定义的类加载器。

双亲委派模型:

双亲委派模型要求除了顶层的启动类加载器外,其他的类加载器都应当有自己的父类加载器。这里类加载器之间的父子关系一般不会以继承关系来实现,而是都使用组合关系来复用父加载器的代码

工作过程:

如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传递到顶层的启动类加载器中,只有当父类加载器反馈自己无法完成这个请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。

好处:

Java类随着它的类加载器一起具备了一种带有优先级的层次关系。例如类Object,它放在rt.jar中,无论哪一个类加载器要加载这个类,最终都是委派给启动类加载器进行加载,因此Object类在程序的各种类加载器环境中都是同一个类,判断两个类是否相同是通过classloader.class这种方式进行的,所以哪怕是同一个class文件如果被两个classloader加载,那么他们也是不同的类。

代码测试时,其实还发现了另外一个问题,就是我初始化了两个classLoader对象,打印出来发现这两个对象的类加载器竟然一模一样,为什么呢?

IAccountDao iAccountDao = new IAccountDaoImpl();
            ClassLoader classLoader2 = iAccountDao.getClass().getClassLoader();
            System.out.println(classLoader2);
            System.out.println(classLoader2.getParent());
ClassLoader classLoader = BeanFactory.class.getClassLoader();
            System.out.println(classLoader);
//执行结果是
//sun.misc.Launcher$AppClassLoader@135fbaa4
//sun.misc.Launcher$ExtClassLoader@2503dbd3
//sun.misc.Launcher$AppClassLoader@135fbaa4

答案参考:
https://blog.csdn.net/weixin_42248137/article/details/80387305

参考:
https://www.cnblogs.com/Yanss/p/11711894.html
https://www.cnblogs.com/fengbs/p/7595849.html
https://blog.csdn.net/YingHuaNanHai/article/details/81264893

你可能感兴趣的:(类加载器)