今天学习工厂模式解耦操作时,误用了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
查询类加载器的种类时发现,自定义的这些类是
image.png
类加载器按照层次,从顶层到底层,分为以下四种:
1.启动类加载器:这个类加载器负责放在<JAVA_HOME>\lib目录中的,或者被-Xbootclasspath参数所指定的路径中的,并且是虚拟机识别的类库加载到内存中。启动类加载器无法被Java程序直接引用。
2.扩展类加载器:这个类加载器由sun.misc.Launcher$ExtClassLoader
实现。它负责<JAVA_HOME>\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
网友评论