动态类加载
我们尝试编写Person类如下
各类方法调用情况
无参方法
有参方法
静态方法
赋值
==所以说Java的类在初始化的时候就会调用静态代码块==
获取类
所以class关键字只对Java类进行加载,并不会进行初始化
==类在实例化的时候会调用构造代码块==
动态加载类
Class.forName()
我们跟进去看看
这里没有,我们可以看看它重载的另一种方法
它是否初始化的值是在这里确定的
也就是说它默认是初始化了的
我们尝试使用这个重载方法进行观察
代码如下
1 | ClassLoader cl = ClassLoader.getSystemClassLoader(); |
这样就被认为是没有初始化后的
输出如下
在这里我们用了一个类加载器,那么如果我们使用这个类加载器去加载类,也就是用它的loadClass方法,它是否会对类进行初始化呢
我们发现输出为空,那么很明显loadClass方法不会对类进行初始化
我们来瞅瞅类加载的具体实现
我们直接打断点调到loadClass方法里
但如果我们打印cl变量就知道,这个加载器是一个app类加载器,我们去这个位置找找它的loadClass方法
但是这个方法是两个参数的,并不符合我们的调用
于是它就去了它的父类,也就走到了我们刚才强制步入的那个loadClass里
但是它又调用了两个参数的loadClass,所以下一步肯定又是回来
然后走到这里,它又调用了它父类的loadClass
然后check它是否被加载过
然后看它的父类是否存在,如果有的话就调用它父类的,如果没有就会去BootstrapClass
我们可以看到在这里的时候它的parent不是null
但是当我们继续往下走然后步入
它又回到了这里
这时如果我们再走到parent这里就会发现parent成null了
那是因为ExtClassLoader里就没有loadClass方法,所以它开始找BootstrapClass,然后还是没有找到
我们在这里继续跟进去
莫名其妙到了URLClassLoader,但我们去查的时候会发现无论是appClassLoader还是ExtClassLoader,它们都没有findClass这个方法
所以就去找了他俩的父类
然后我们走到了这里
我们发现它是在
这个时候完成的类加载
我们跟进去发现它又调用了另一个defineClass
它又跑到SecureClassLoader里了
显而易见的又是父类
然后它又调用了另一个重载的defineClass
最后发现它又回到了ClassLoader里
我们重点关注这个
发现它是一个native方法,也就是在这里完成了类加载
从这里的返回值c也就能很好的看出来了
然后层层返回,就结束了
继承关系如下:
ClassLoader->SecureClassLoader->URLClassLoader->AppClassLoader
调用关系
loadClass->findClass->defineClass(从字节码加载)
URLClassLoader任意类加载
我直接用我最开始学Java语法时候生成的.class文件了
1 | URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("file:///D:\\codefile\\java\\")}); |
我们这里的协议还可以换成http用来加载远程类,如果我们平时做题能控制到这里,那么就很有趣了
defineClass加载
代码如下
1 | Method defineClassMethod = ClassLoader.class.getDeclaredMethod("defineClass",String.class,byte[].class,int.class,int.class); |
急了,写ClassLoader的时候选错包了,运行好几次才发现跑util下去了,应该选java.lang的那个
unsafe.defineClass字节码加载(spring中可以直接生成
它里面也有一个defineClass方法
最终代码如下
1 | Class c = Unsafe.class; |