来给大伙儿画个图,讲讲JVM里类加载和初始化的全过程。类加载其实可以看成是装货、链接、初始化这三部曲,链接里头又分验证、准备、解析三步。整个过程像流水线一样环环相扣,哪个环节出岔子就会报NoClassDefFoundError或者LinkageError。 先说说怎么把类装进内存。虚拟机得先在磁盘上找到.class文件,把它读进内存里,这就好比客人进门了,但这时候还没干活儿呢。 接下来是链接阶段。这时候虚拟机要干三件事儿:验证一下字节码符不符合规范,免得让“黑客手写”的坏代码混进来;准备好所有静态变量的内存空间,先给它们赋个默认值;还有就是把符号引用变成直接引用,也就是把类名、方法名这些查表找到对应的内存地址。 到了初始化阶段才算是真正的干活儿。这时候才会执行static代码块还有给静态变量赋值的语句,把程序员定义的初始值“喂”进静态字段。记住哦,初始化只能做一次,别以为叫它两次它就会多跑一趟。 关于什么时候会触发初始化,这里面有讲究。不是每次引用类都能让它运行起来。JVM定了规矩,只有六种情况才会把类拉进来干活儿:用new关键字造对象、访问或者给静态字段赋值、调用静态方法、通过Class.forName()显式加载、初始化子类的时候顺带把父类也初始化一下、或者JVM启动时标记的那个启动类。只要碰到这几条中的任意一条,类就跑不脱啦。 加载的终点可不是简单地把字节码扔进堆里就完事了。它得在堆区生成一个java.lang.Class对象来做张“通行证”,程序员就是靠这张通行证去查方法区里的数据。换句话说没有这个Class对象,反射机制根本没法玩起来。 最后说说加载器的分工情况。Java把谁去加载也给分了层:最底层的是Bootstrap ClassLoader,是用C++写的,专门管$JAVA\_HOME/jre/lib/rt.jar里的那些核心库;往上是Extension ClassLoader管扩展包;再往上是App ClassLoader负责classpath里的东西;最后一层是Custom ClassLoader让开发者自己折腾。Tomcat、JBoss这种服务器常常用它来隔离不同的部署包实现热部署啥的。 这里头有个“双亲委派”的规矩很关键:AppClassLoader想干活儿时得先让BootstrapClassLoader试试;如果上级找不到才轮到自己动手。这样一来既保住了核心API不被改坏,也保证了一个类在虚拟机里只被加载一次。