java代码块执行顺序由jvm严格控制:静态成员按源码从上到下执行,跨类静态依赖易失效,禁止static中获取spring bean,复杂逻辑应延迟初始化,实例字段初始化避免调用子类可重写方法。

Java代码块执行顺序不是“谁写在前面谁先跑”,而是由JVM严格按规则驱动的。搞错顺序,空指针、默认值、类加载失败这些Bug往往藏在看似无害的赋值语句里。
静态成员必须按源码顺序声明
静态变量赋值和static{}块混合出现时,JVM只认文本位置——从上到下逐行执行。比如:
- 错误写法:先写
static Service s = createService();,再声明static Config cfg;,而createService()内部又用到了cfg→ 此时cfg还是null - 正确做法:把
cfg声明放在s之前,并显式初始化,如static Config cfg = loadConfig();
别在静态块里访问子类字段
父类静态块中直接引用Child.NAME,会强制触发子类初始化;但此时子类自己的静态变量还没轮到赋值,仍为默认值(如String为null,int为0)。
- 典型表现:输出
null而非预期字符串,或数值为0 - 本质原因:JVM按文本顺序执行子类静态初始化,字段声明处的默认值先于赋值语句生效
- 规避方式:避免跨类静态依赖;如必须共享配置,改用独立配置类或延迟加载
实例字段初始化不调用可重写方法
在字段声明处(如int x = getValue();)或构造器开头前调用的方法,若被子类重写,会触发子类逻辑——但此时子类字段尚未初始化,仍是默认值。
- 常见问题:父类字段依赖子类方法返回值,结果得到
0或null - 安全做法:字段初始化只用常量、final变量或私有静态工具方法
- 更推荐:用
final字段 + 构造器参数注入,或把复杂逻辑移到静态工厂方法中
复杂逻辑一律延迟初始化
所有静态初始化最终编译成一个叫<clinit></clinit>的字节码方法,它是线性、单次、零容错的:前面出错,后面全跳过,类直接不可用。
- I/O、网络、反射、JSON解析等操作,坚决不能放进去
- 配置加载、连接池创建等,移到首次使用时的懒加载中(如双重检查锁单例)
- 用
javap -c YourClass检查<clinit></clinit>是否臃肿,超20条指令就该拆分
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











