静态代码块在类首次主动使用时执行一次,属于类初始化阶段(),按源码顺序执行静态变量赋值和static{};构造方法每次new对象时执行,属实例初始化,按父类到子类顺序完成字段赋值、实例块、构造方法。

静态代码块在类首次主动使用时执行一次,构造方法每次 new 对象时执行;关键不是“谁先谁后”,而是“谁在什么阶段、以什么身份运行”——静态属于类,构造属于对象,二者不在同一初始化层级。
静态代码块只跑一次,且必须最先完成
它在类初始化阶段(<clinit></clinit>)执行,所有静态变量赋值和 static{} 按源码从上到下交替运行。一旦某个语句抛出未捕获异常(比如空指针或 IO 错误),整个类初始化失败,后续任何调用都会直接报 ExceptionInInitializerError。
- 把配置加载、单例创建、驱动注册等一次性逻辑放在这里,但别做耗时或不稳定的操作(如网络请求、文件读取)
- 静态字段和静态块之间有依赖时,声明顺序决定执行顺序:想用
cfg初始化service,就得先把cfg写在前面 - 避免在静态块里调用子类可能重写的方法,也不要用
this或new当前类实例,此时对象根本不存在
构造方法体在实例代码块之后才开始运行
每次 new 对象时,JVM 先执行父类构造代码块 → 父类构造方法 → 子类构造代码块 → 子类构造方法。构造方法本身不是初始化起点,而是终点——它前面已经完成了字段赋值、实例块执行、父类初始化等全部前置动作。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 字段直接赋值(如
String name = "guest";)和构造代码块按源码顺序混合执行,本质等价,选一种风格保持一致即可 - 不要在字段初始化表达式、构造代码块或构造方法第一行就调用非 final 方法——此时子类字段还是默认值(
null、0),容易引发空指针或逻辑错误 - 如果必须复用初始化逻辑,用构造代码块封装共性操作(如设置状态、生成 ID),复杂业务仍留在构造方法体内
父子类混合场景下,顺序不可跳过、不可倒置
执行 new Child() 时,静态部分(父→子)只走一遍;之后每次 new 都重复实例部分(父→子)。这意味着:父类构造方法执行期间,子类的任何实例字段都还没赋值,全是默认值。
- 父类构造器中若调用了被子类重写的方法,该方法访问子类字段会得到
null或0,而不是你预期的初始值 - 解决办法是把可重写逻辑移到
@PostConstruct或延迟初始化方法中,或者用 final 字段 + 构造参数明确传递依赖 - 调试时可在四类位置加日志标记:[S]静态块、[I]实例块、[C]构造方法首行、[F]字段初始化方法,观察输出是否符合“父静→子静→父实→父构→子实→子构”链条
真正危险的不是顺序本身,而是跨阶段误用
常见错误不是记错执行步骤,而是混淆了“类存在”和“对象存在”的边界。静态块里试图获取 Spring Bean、构造方法里调用未 final 的子类方法、字段初始化时引用尚未声明的静态变量——这些都不是顺序错了,而是用错了上下文。
- 静态上下文中禁止依赖 IoC 容器,改用 Holder 模式或
@PostConstruct - 实例初始化阶段慎用多态,优先用 final 字段 + 构造注入,让依赖清晰可见
- 复杂初始化逻辑不要塞进
<clinit></clinit>,用静态工厂方法或 Builder 分离准备与构建
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










