类加载器的结构:
BootStrapClassLoader是用本地代码所写
ExtClassLoader是标准扩展类加载器
AppClassLoader系统类加载器
由上至下,上一个是下一个的parent。
类加载的顺序为:加载,链接,初始化。
链接又包括了验证,准备,解析的步骤。其中解析是可选的。
1.校验:对二进制字节码的格式进行校验,二进制字节码的格式校验遵循Java Class File Format(具体请参见JVM规范)规范,如格式不符合则抛出VerifyError
2.准备:为该类型分配它所需的内存,比如为类变量分配内存,JVM初始化类中的静态变量,并将其值赋为默认值
3.解析:将常量池中的符号引用转成直接引用(可选的),具体是:对类中的所有属性、方法进行验证,以确保其需要调用的属性、方法存在,以及具备相应的权限(例如public、private域权限等),这个阶段失败可能会造成NoSuchMethodError、NoSuchFieldError等错误信息。不同的Java虚拟机实现允许在程序执行的不同时间进行解析,实现可以选择预先解析所有的符号引用,从初始类开始,到后续的各个类,直到所有的符号应用都被解析了。在这种情形中,程序的在它的main()方法伤未被调用时就已经完全连接了,这种方法被称为早解析。另外一种方式是,实现可以在 访问每一个符号应用的最后一刻才解析它。在这种情形中,Java虚拟机只会在执行程序第一次用到这个符号引用的时候去解析它,这种方法称为迟解析。不管是早解析还是迟解析,对于特定的Java虚拟机来说,都在程序执行过程中第一次实际试图访问一个符号引用的时候才抛出错误。
这里边就涉及到类初始化时机,即什么时候才初始化一个类呢?书上是这样说的:java虚拟机只有在程序首次主动使用一个类或接口时才会对其进行初始化,只有6种活动被看作是程序对类的主动使用:
1.创建类的实例
2.调用类的静态方法
3.访问某个类或接口的静态变量,或者对该静态变量赋值
4.调用类的反射方法,如:Class.forName() (ClassLoader.loadClass()方法是不会初始化的)
5.初始化一个类的子类,可以看作是对父类的主动使用,所以会初始化父类
6.jVM启动时旁观标明为启动类的类。如在命令窗口执行:java A,则A就是启动类,JVM会最先初始化它。