java类初始化顺序为:先父类静态变量与静态块(按源码顺序),再子类静态部分;创建对象时先父类实例变量与块、构造器,再子类对应部分。

Java中对象初始化顺序是排查类加载和构造问题的关键。静态变量(包括静态块)按类声明顺序执行,且父类优先于子类;非静态部分则在实例化时才触发,同样遵循父类先于子类的原则。
静态变量和静态块的执行顺序
静态成员属于类,只在类首次被主动使用(如首次 new、调用静态方法、访问静态字段等)时初始化,且仅执行一次。JVM按源码中声明的先后顺序执行静态变量赋值和静态块,但父类的静态内容一定先于子类执行,无论子类是否直接引用父类静态成员。
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- 若父类未被主动使用过,而子类首次被使用,则先触发父类的静态初始化,再执行子类的静态初始化
- 静态变量初始化表达式中若调用方法,该方法内对本类其他静态变量的访问,可能因尚未执行到对应语句而得到默认值(如 int 为 0,Object 为 null)
- 可通过在每个静态块开头加 System.out.println("类名.静态块") 快速定位执行时机
实例初始化的实际触发时机
new 一个子类对象时,会依次触发:
→ 父类静态 → 子类静态 → 父类实例变量默认值 → 父类实例变量显式赋值 / 实例块 → 父类构造器 → 子类实例变量默认值 → 子类实例变量显式赋值 / 实例块 → 子类构造器
- 注意:实例变量的显式赋值(如 String name = "Tom";)和实例块({ System.out.println("实例块"); })都按代码顺序执行,且在对应构造器之前
- 若父类构造器中调用了被子类重写的方法,此时子类实例变量可能还未初始化(仍为默认值),容易引发空指针或逻辑错误
- 可在每个构造器第一行加 System.out.println("类名.构造器"),配合实例块日志,观察完整流程
用 -XX:+TraceClassLoading 查看类加载时机
启动 JVM 时加上该参数,可打印出类被加载和初始化的时间点,帮助确认“哪个类先触发”、“静态块是否真的执行了”。例如:
-XX:+TraceClassLoading -Djava.security.manager
- 输出类似:[Loaded ParentClass from ...] 表示类加载完成(此时静态初始化尚未开始)
- 紧接着出现 [Initialized ParentClass] 才代表静态块已执行完毕
- 对比 ParentClass 和 ChildClass 的初始化日志顺序,就能验证继承链上的静态执行顺序
常见误判与调试建议
很多“顺序异常”其实源于对“主动使用”的误解,比如只是声明了子类类型变量但未 new 或访问静态成员,父类静态块就不会执行。
- 避免在静态初始化中依赖尚未初始化的其他静态变量(尤其是跨类依赖)
- 不要在父类构造器中调用可能被重写的方法,除非明确设计为模板方法且子类字段已就绪
- 使用 IDE 调试时,在静态块、构造器、实例块处设断点,单步执行最直观
- 简单复现场景:写两个类,各自含静态块、实例块、构造器,各打一行 log,运行 new 子类,观察控制台输出顺序
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










