- 自低向上检查该类是否已经加载
- 自顶向下进行实际查找和加载
- 这里用双亲委派,主要出于安全来考虑,不会让随意的class加载进去
自定义的类加载器
继承ClassLoader,重写findclass()
public class MyClassLoader extends ClassLoader {
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
File f = new File("c:/test/", name.replace(".", "/").concat(".class"));
try {
FileInputStream fis = new FileInputStream(f);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int b = 0;
while ((b=fis.read()) !=0) {
baos.write(b);
}
byte[] bytes = baos.toByteArray();
baos.close();
fis.close();//可以写的更加严谨
return defineClass(name, bytes, 0, bytes.length);
} catch (Exception e) {
e.printStackTrace();
}
return super.findClass(name); //throws ClassNotFoundException
}
}
这边自己实现findClass
方法用的是钩子函数,模板方法的设计模式。
类的LazyLoading
LazyLoading 五种情况
–new getstatic putstatic invokestatic指令,访问final变量除外
–java.lang.reflect对类进行反射调用时
–初始化子类的时候,父类首先初始化
–虚拟机启动时,被执行的主类必须初始化
–动态语言支持java.lang.invoke.MethodHandle解析的结果为REF_getstatic REF_putstatic REF_invokestatic的方法句柄时,该类必须初始化
public class LazyLoading { //严格讲应该叫lazy initialzing,因为java虚拟机规范并没有严格规定什么时候必须loading,但严格规定了什么时候initialzing
public static void main(String[] args) throws Exception {
//P p;
//X x = new X();
//System.out.println(P.i);
//System.out.println(P.j);
//Class.forName("com.mashibing.jvm.c2_classloader.T008_LazyLoading$P");
}
public static class P {
final static int i = 8;
static int j = 9;
static {
System.out.println("P");
}
}
public static class X extends P {
static {
System.out.println("X");
}
}
}
当执行X x = new X();
输出:
P
X
当访问final static 变量,类不用加载。
当执行XSystem.out.println(P.i);
输出:
8
当执行System.out.println(P.j);
输出:
P
9
当执行Class.forName("com.mashibing.jvm.c2_classloader.T008_LazyLoading$P");
输出:
P
整个加载过程:
Loading
Linking
2.1. Verification
- 验证文件是否符合JVM规定
2.2. Preparation
- 静态成员变量赋默认值
2.3. Resolution
- 将类、方法、属性等符号引用解析为直接引用
常量池中的各种符号引用解析为指针、偏移量等内存地址的直接引用-
Initializing
- 调用类初始化代码 <clinit>,给静态成员变量赋初始值
小总结:
- load - 默认值 - 初始值
- new - 申请内存 - 默认值 - 初始值