1.类的加载过程
类的加载过程分为三个步骤:装载,链接(验证、准备、解析),初始化。
装载:查找并加载类的二进制数据。
链接:
(1)验证:是链接阶段的第一步,这一阶段的目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
(2)准备:正式为类变量分配内存并设置类变量初始值的阶段。
(3)解析:虚拟机将常量池内的符号引用替换为直接引用的过程。
初始化:根据程序员通过程序制定的主观计划去初始化类变量和其他资源。
2.类的加载
JDK提供三种类加载器:
1)Bootstrap ClassLoader(启动类加载器):负责加载JAVA_HOME/jre/lib里的jar包,由C语言实现,该类的getClassLoader()方法返回值为null;String类和Object类的类加载器都是Bootstrap ClassLoader。
执行如下代码:
public static void main(String[] args) {
System.out.println(String.class.getClassLoader());
结果如下:
null
2)ExtClassLoader(扩展类加载器):主要加载java核心扩展类,即JAVA_HOME/jre/ext目录下的jar文件。
3)APPClassLoader(应用类加载器):加载CLASSPATH中的所有jar文件和目录下的class。
如加载用户自定义的People类,代码如下:
public static void main(String[] args) {
ClassLoader c = People.class.getClassLoader();
System.out.println(c);
运行结果如下:
sun.misc.Launcher$AppClassLoader@b4aac2
三者关系:
执行如下代码:
public static void main(String[] args){
ClassLoader c = People.class.getClassLoader();
System.out.println(c);
System.out.println(c.getParent());
System.out.println(c.getParent().getParent());
结果如下:
sun.misc.Launcher$AppClassLoader@b4aac2
sun.misc.Launcher$ExtClassLoader@154617c
null
由此可知Bootstrap ClassLoader是ExtClassLoader的父类,ExtClassLoader是APPClassLoader的父类。
双亲委派模型:
(1)当前类加载器查询自己是否已加载过此类,如果加载过则直接返回已经加载过的此类。
(2)若没有加载过,则委托其父类去加载,父类同样先检查自己是否加载过此类,若加载过直接返回,若没有则继续委托其父类去加载,直到委托到Bootstrap ClassLoader,它也采取与之前同样的操作。
(3)若Bootstrap ClassLoader也没有加载过该类,则尝试加载,检查该类是否属于自己的加载范围,若属于则加载并返回,若不属于则委托给其子类尝试加载,子类进行同样的操作,若继续加载失败则由APPClassLoader尝试加载,若继续失败,则抛出异常。
使用双亲委派模型的好处:
(1)安全性,避免用户自己写的类动态替换java的一些核心类。
(2)避免重复加载。
自定义类加载器
自定义类加载器需要继承ClassLoader类,该源码中关键的三个方法:
loadClass:以双亲委托方式加载类
findClass:根据类的包路径找到class文件
defineClass:从class字节码中加载Class对象
编写一个自定义类加载器需要重写findClass方法。