文章目录
类加载和初始化
class cycle
首先一个class文件在硬盘里面
然后JVM去对它进行以下行为:
- Loading,把class文件load到内存,双亲委派(安全)
- Linking,分三小步:
- verification,校验文件是否复合JVM规定的class格式.
- preparation,静态变量赋默认值,比如int-0,double-0.0,boolean-false
- resolution,解析,
将类、方法、属性等符号引用解析为直接引用
如常量池中的各种符号引用解析为指针、偏移量等内存地址的直接引用
- Initializing,初始化,这时候静态变量赋初始值(代码指定的值),调用静态代码块
一般我们只要记住,静态变量的初始化分为两步,Linking时是默认值,Initializing后才是初始化的值
成员变量的初始化其实也是分两步,第一步申请内存空间时是默认值,第二步调用构造方法时才是初始化的值
类加载器ClassLoader
一个class文件的Loading,load出两个东西:
1.class文件的二进制编码加载到内存
2.生成一个与之对应的Class对象,改对象指向内存中的class编码文件
如果打印一下String的ClassLoader.会发现结果为null:
System.out.println(String.class.getClassLoader());
这是因为:
最顶层的加载器Bootstrap是用C++来实现的,在JAVA中没有与之对应的类.
所以Bootstrap加载出来的类,比如String,获取到的ClassLoader是null.
类加载器的分层关系:
注意
1.这个上层加载器,即父加载器,是逻辑上的关系,其实就是一个成员变量
2.不是类的继承关系,那是另一种维度的关系
3.加载器也是一个对象,也要由另一个加载器加载,但并不一定是由他的parent加载,是谁不一定.最终都是由Bootstrap加载的
设一个加载器a的上层加载器是b,那么 a不一定是被b加载的
求证一下BootStrap,Ext,App都加载哪些类
其实AppClassLoader和ExtClassLoader都是sun.misc.Launcher的内部类
(JDK11把Launcher换成了ClassLoaders)
Launcher里面可以看到这三个内部类,和他们各自加载的path
BootClassPathHolder:System.getProperty(“sun.boot.class.path”)
AppClassLoader:java.class.path
ExtClassLoader:java.ext.dirs
我们写个方法打印一下这些东西就看出来了:
public static void main(String[] args) {
System.out.println("boot-------------------");
System.out.println(System.getProperty("sun.boot.class.path").replaceAll(":", System.lineSeparator()));
System.out.println("ext-------------------");
System.out.println(System.getProperty("java.ext.dirs").replaceAll(":", System.lineSeparator()));
System.out.println("classpath-------------------");
System.out.println(System.getProperty("java.class.path").replaceAll(":", System.lineSeparator()));
}
双亲委派
是什么
双亲委派并不是指父母双方,而是指"查找类时从子到父,加载类时从父到子"的这么一个机制.
具体含义:
众所周知,ClassLoader加载完一个类后,会放入一个ClassCache,下次再用时就不需重复加载了.每个ClassLoader有自己的ClassCache
- 当我们需要找一个类时,会先交给最下层的ClassLoader,在ClassCache找,如果找到了就返回结果,如果找不到就交给上层加载器,上层加载器进行同样的操作,直到Bootstrap.
- 真正去加载这个类的时候,会自上到下开始加载.
每个ClassLoader先看自己管辖的类里面有没有需要加载的class,如果有就加载返回,如果没有就交给下一层去加载.
如果都没有就抛异常CLassNotFoundException.
为什么
为啥不直接放到一个ClassCache里面,这样就不用层层查找了啊?
这里主要是出于安全考虑.
假设黑客小明自定义了一个java.lang.String对象,里面做了些非法操作;如果不分层查找的话,用户就会用到他自定义的String,阴谋得逞;
双亲委任机制下,使用String时,先看看父加载器是否已加载,直到找到Bootstrap后直接返回String类.
可以打破双亲委派机制吗?
可以,自定义一个classLoader,重写loadClass方法就可以打破.
热加载/热部署的时候,可能会重写loadClass(),打破双亲委派机制
从源码角度去理解ClassLoader
- 继承ClassLoader(这里用到了模板方法设计模式)
- 重写模板方法findClass,调用defineClass
- 自定义类加载器 加载 加密的class,防止反编译和篡改
ClassLoader简单读源码
别的方法ClassLoader类已经写好啦(模板设计模式),
关键点就是下面的findClass方法,该方法直接抛异常,是个必须被子类重写的方法.(模板方法,钩子函数)
protected Class<?> loadClass(String name,