个人笔记--Java类加载

java类加载流程:

其实是通过ClassLoader(类加载器)去把对应的class文件类型动态加载到JVM中。

其中类加载器有三个,在sun.misc.Launcher这个Java虚拟机的入口应用里,Launcher初始化ExtClassLoader和AppClassLoader。并通过System.getProperty(“sun.boot.class.path”)得到了BootStrapClassLoader。

加载顺序:

BootStrapClassLoader(启动类加载器):通过sun.boot.class.path(可修改加载路径)加载JRE目录下的jar包和class文件。

ExtClassLoader(扩展类加载器):通过java.ext.dirs(可修改加载路径)加载lib\ext。

AppClassLoader(系统类加载器):加载当前java工程目录bin下的class文件。

AppClassLoader的parent是ExtClassLoader,ExtClassLoader的parent是null。

Bootstrap ClassLoader是由C/C++编写的,它本身是虚拟机的一部分,所以它并不是一个JAVA类,也就是无法在java代码中获取它的引用。

Bootstrap没有父加载器,但是它却可以作用一个ClassLoader的父加载器。比如ExtClassLoader。

双亲委托机制:

扫描二维码关注公众号,回复: 1041445 查看本文章

一个类加载器查找class和resource时,是通过“委托模式”进行的,它首先判断这个class是不是已经加载成功,如果没有的话它并不是自己进行查找,而是先通过父加载器,然后递归下去,直到Bootstrap ClassLoader,如果Bootstrap classloader找到了,直接返回,如果没有找到,则一级一级返回,最后到达自身去查找这些对象。

委托是从下向上,然后具体查找过程却是自上至下。

重要方法:

loadClass():

执行findLoadedClass(String)去检测这个class是不是已经加载过了。 
执行父加载器的loadClass方法。如果父加载器为null,则jvm内置的加载器去替代,也就是Bootstrap ClassLoader。这也解释了ExtClassLoader的parent为null,但仍然说Bootstrap ClassLoader是它的父加载器。
如果向上委托父加载器没有加载成功,则通过findClass(String)查找。 如果class在上面的步骤中找到了,参数resolve又是true的话,那么loadClass()又会调用resolveClass(Class)这个方法来生成最终的Class对象。

双亲委托源代码:

protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException{
        synchronized (getClassLoadingLock(name)) { // 首先,检测是否已经加载 Class<?> c = findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); try { if (parent != null) { //父加载器不为空则调用父加载器的loadClass c = parent.loadClass(name, false); } else { //父加载器为空则调用Bootstrap Classloader c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) {  } if (c == null) { long t1 = System.nanoTime(); //父加载器没有找到,则调用findclass c = findClass(name); sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if (resolve) { //调用resolveClass()  resolveClass(c); } return c; } }

不管是Bootstrap ClassLoader还是ExtClassLoader等,这些类加载器都只是加载指定的目录下的jar包或者资源。如果在某种情况下,我们需要动态加载一些东西呢?比如从D盘某个文件夹加载一个class文件,或者从网络上下载class主内容然后再进行加载,这样可以吗?

如果要这样做的话,需要我们自定义一个classloader。

步骤:

编写一个类继承自ClassLoader抽象类。
复写它的findClass()方法。
在findClass()方法中调用defineClass()。
注意:如果要编写一个classLoader的子类,也就是自定义一个classloader,建议覆盖 findClass()方法,而不要直接改写 loadClass()方法。
 

defineClass():

这个方法在编写自定义classloader的时候非常重要,它能将class二进制内容转换成Class对象,如果不符合要求的会抛出各种异常。

 

Context ClassLoader:线程上下文类加载器

查看Thread.java源码可以发现:

ContextClassLoader只是一个成员变量,通过setContextClassLoader()方法设置,通过getContextClassLoader()设置。

每个Thread都有一个相关联的ClassLoader,默认是AppClassLoader。并且子线程默认使用父线程的ClassLoader除非子线程特别设置。

猜你喜欢

转载自www.cnblogs.com/kz2017/p/9093044.html
今日推荐