Java类初始化时静态成员按源码顺序交替执行,父类优先于子类;静态代码块在类首次主动使用时执行,编译期常量不触发初始化;new对象时先完成父子类静态初始化,再执行实例化流程。
Java 类加载过程中,静态成员的执行顺序不是随意安排的,而是由 JVM 类加载机制严格规定的初始化阶段决定的。核心在于:类初始化只发生一次,且静态代码块和静态变量赋值按源码顺序交替执行,父类优先于子类。
静态代码块在类的「初始化阶段」执行,前提是该类尚未初始化,且被首次主动使用。所谓“主动使用”包括:
注意:ClassLoader.loadClass("X") 不会触发初始化;而 static final 基本类型常量(如 public static final int PORT = 8080)属于编译期常量,直接内联,不触发类初始化,也就跳过静态代码块。
同一类中,静态变量显式赋值(static int a = getValue();)和静态代码块(static { ... })按源码书写顺序从上到下交替执行,不是“先全赋值、再全执行块”。例如:
立即学习“Java免费学习笔记(深入)”;
static int x = f1();,后写 static { y = f2(); },则 f1() 先调用f1() 中访问了后面才声明的 static String z,此时 z 还未初始化,值为 null 或默认值(如 0),不会报编译错误,但可能引发逻辑异常子类初始化必然触发父类初始化,但反过来不成立。具体表现为:
Child.staticMethod() → 先加载并初始化 Parent(执行其全部静态成员),再初始化 Child
Parent.STATIC_FIELD → 只初始化 Parent,Child 完全不参与ExceptionInInitializerError)→ 整个类进入“初始化失败”状态,后续任何主动使用都会直接抛出同一异常当执行 new Child(),整个流程分两层展开:
其中,实例块(即构造块 { ... })每次都在对应构造器体之前执行,属于对象级初始化,与静态内容完全隔离。