类加载机制实践
系统调用一个class,比如xx.class去查看xx类的信息的时候:
1,loadClass,先在loadedClass里找,找不到,就交给父类去load试试,如果父类还是找不到(返回null),就去调用findClass试试
2,findClass去找未加载进内存的.class方法,如果找到了,就做defineClass的步骤,把它加载进内存
3,defineClass本身是一个native方法,在这个native方法里,当解析到方法的父类的时候(每个方法都有父类,要么是自定义的,要么是Object),会回调当前classLoader的loadClass方法,然后loadClass方法会重复1-3步骤,寻找到父类以后,把父类交出去。
按照我的理解,defineClass里所做的把.class文件转化成loadedClass的步骤,就包括了类加载生命周期里的加载,验证,解析步骤
其中findClass里去寻找.class文件(或者其他的二进制流的方法)属于加载的第一步,“1,通过一个类的全限定名来获取定义此类的二进制流。 ”
defineClass这个native属于加载的第二步“2,把字节流所代表的静态存储结构转化为方法区的运行时数据结构”到这里都属于类加载中的加载阶段。
在这个过程中,defineClass回调当前类的loadClass方法来寻找被加载类的父类,这个过程是验证and解析的过程,属于类加载中的连接(验证,准备,解析)阶段。这里找到父类以及其他相关的各种引用的类之后,就已经把符号引用转化为直接引用了,所以到这里递归完以后,连接阶段就结束了
也就是说,在我们看来,调用defineClass的时候,等于把类给初始化了,类的加载,连接(验证,准备,解析)阶段+初始化等,都执行了。
绕回来,代码
这里做了一个类的热编译+热加载的功能
自定义类加载器
这里分两种情况,
1,代码是main方法运行的情况下(非web应用),默认的类加载器是appClassLoader,这种情况下,我们写的.java文件会