Skip to content

Latest commit

 

History

History
145 lines (104 loc) · 3.95 KB

18-7-31.md

File metadata and controls

145 lines (104 loc) · 3.95 KB

2018.07.31

Review Java类加载过程

类加载的过程包括:

  1. 加载,通过一个类的完全限定查找此类的字节码文件,创建一个Class对象
  2. 链接,首先验证字节码的完整性与安全性;然后为静态域分配内存空间,为静态成员赋默认值(0nullfalse);最后如果有必要的话,解析这个类创建的对其他类的所有引用。
  3. 初始化,若该类有父类,对其进行初始化,执行静态初始化器,初始化静态成员变量。

代码测试

测试类Test

class Test {

    final static int b = 7;

    static int a = 5;

    static {
        System.out.println("static block");
    }

    {
        System.out.println("Test Initialize");
    }

    public Test() {
        System.out.println("Test Constructor");
    }
    
}

Test包括:

  1. 一个编译期静态常量final static int b = 7;
  2. 一个静态成员变量static int a = 5;
  3. 静态初始化代码块static{...}
  4. 初始化代码块{...}
  5. 构造函数public Test(){...

测试类加载阶段

输出编译期静态常量b

    public static void main(String[] args) {
        System.out.println("Test Load");
        System.out.println(Test.b);
    }

output:
Test Load
7

从输出结果可以看出,只输出b,未输出其他初始化代码块内容,所以没有触发类的初始化。

获取Class:

    public static void main(String[] args) {
        System.out.println("Get Test Class");
        Class c = Test.class;
    }

output:
Get Test Class

输出结果说明未触发类的初始化,只是触发了类的加载。

测试类初始化阶段

输出静态成员变量a

    public static void main(String[] args) {
        System.out.println("Test Initialize");
        System.out.println(Test.a);
    }

output:
Test Initialize
static block
5

输出结果说明,在调用一个类的静态成员变量时,会触发类的初始化。

连续两次输出a

    public static void main(String[] args) {
        System.out.println("Test Initialize");
        System.out.println(Test.a);
        System.out.println(Test.a);
    }

output:
Test Initialize
static block
5
5

输出结果说明,一个类的静态代码块只在第一次初始化时调用。

创建一个Test实例:

    public static void main(String[] args) {
        System.out.println("Create Test Instance");
        Test test = new Test();
    }

output:
Create Test Instance
static block
Test Initialize
Test Constructor

输出结果说明:

  1. 创建类的实例会触发类的初始化。
  2. 静态代码块、初始化代码块、构造函数的触发顺序为:静态代码块->初始化代码块->构造函数。

总结

触发类加载的初始化阶段主要有以下几种:

  • 创建类实例、调用类的静态字段(不包括编译期常量)、调用类的静态方法时。

  • 使用反射包(java.lang.reflect)的方法对类进行反射调用时,如果类没有初始化,触发类的初始化阶段。

  • 当初始化一个类的时候,如果其父类还没进行初始化则需先触发其父类的初始化。

  • 当Java虚拟机启动时,用户需要指定一个要执行的主类(包含main方法的类),虚拟机会先初始化这个主类。

  • 当使用JDK 1.7 的动态语言支持时,如果一个java.lang.invoke.MethodHandle 实例最后解析结果为REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法句柄对应类没有初始化时,必须触发其初始化。

其他小结:

  • 使用.class方式获取Class文件只触发类的加载阶段,而getClass()Class.forName()都会触发类的初始化。

  • 静态代码块在类加载的初始化阶段执行,只会在第一次初始化执行。

  • 创建类的实例时会执行类的初始化代码块和构造函数,初始化代码块在构造函数之前执行。