一、类初始化的触发时机
在Java程序中,当一个类被使用时,它就会被JVM自动初始化,在初始化过程中,JVM将会为该类分配内存空间,并为类中的静态变量赋值。具体来说,Java程序中会触发以下4种情况:1. 当创建类的实例时,如果该类还没有被初始化,则JVM会自动执行该类的初始化过程;
public class Person { static { System.out.println("Person类的初始化过程"); } public Person() { System.out.println("创建Person类的实例"); } public static void main(String[] args) { new Person(); } }
运行结果为:
Person类的初始化过程 创建Person类的实例
2. 当访问类的静态变量或静态方法时,如果该类还没有被初始化,则JVM会自动执行该类的初始化过程;
public class Person { public static int age = 20; static { System.out.println("Person类的初始化过程"); } public static void main(String[] args) { System.out.println(Person.age); } }
运行结果为:
Person类的初始化过程 20
3. 当使用反射方式调用类的静态方法时,如果该类还没有被初始化,则JVM会自动执行该类的初始化过程;
public class Person { static { System.out.println("Person类的初始化过程"); } public static void hello() { System.out.println("Hello World!"); } public static void main(String[] args) throws Exception { Class cls = Class.forName("Person"); Method method = cls.getMethod("hello"); method.invoke(null); } }
运行结果为:
Person类的初始化过程 Hello World!
4. 当初始化一个类的子类时,如果该类还没有被初始化,则JVM会自动执行该类的初始化过程。
二、类初始化的过程
一个类的初始化过程,通常包括以下几个步骤:1. 加载:在加载阶段,JVM会从磁盘或网络中读取.class文件的二进制数据,并将这些数据转换成JVM可用的数据结构,并保存在方法区中。
2. 验证:在验证阶段,JVM会对类的二进制数据进行格式验证、语义验证、字节码验证和符号引用验证等一系列的检查,以确保类的正确性和安全性。
3. 准备:在准备阶段,JVM会为类中的所有静态变量分配内存空间,并为它们赋上默认值,静态成员变量被赋值为0,静态引用变量被赋值为null。
4. 解析:在解析阶段,JVM会将类中所有符号引用转换为直接引用,例如将类、方法等符号引用转换为内存地址。
5. 初始化:在初始化阶段,JVM会依次执行类中的静态代码块和静态初始化语句,并为静态变量赋予正确的值。在JVM启动时或首次使用类时,将会触发类的初始化过程,JVM会确保一个类的初始化在多线程环境下是线程安全的。
三、类初始化的顺序
在一个类的初始化过程中,类变量和静态代码块的初始化先后顺序是由它们在代码中出现的先后顺序决定的,而且子类的初始化一定要在父类初始化结束后才会开始。下面是一个示例代码:public class Person { static { System.out.println("Person的静态代码块1"); } public static Person person = new Person(); static { System.out.println("Person的静态代码块2"); } public Person() { System.out.println("Person的构造方法"); } } public class Student extends Person { static { System.out.println("Student的静态代码块1"); } public static Student student = new Student(); static { System.out.println("Student的静态代码块2"); } public Student() { System.out.println("Student的构造方法"); } public static void main(String[] args) { new Student(); } }
运行结果为:
Person的静态代码块1 Person的构造方法 Person的静态代码块2 Student的静态代码块1 Student的构造方法 Student的静态代码块2
四、类初始化的注意事项
在类的初始化过程中,需要注意以下几个方面:1. 静态变量初始化时可以引用到类中定义的静态变量和静态方法,但是在初始化过程中不能引用该类的对象或实例方法。
public class Person { public int age = 20; static { System.out.println("Person的静态代码块"); // 下一行代码会编译错误,不能在静态代码块中引用该类的实例方法 // hello(); } public static void hello() { System.out.println("Hello World!"); } public static void main(String[] args) { hello(); } }
运行结果为:
Person的静态代码块 Hello World!
2. 类的初始化是由JVM执行的,而且只会执行一次,多个线程在同一时刻只会有一个线程执行类的初始化过程。
3. 类的初始化可以使用synchronized关键字或者volatile关键字来保证线程安全。