Java 静态代码块与代码块详解
1. 引言在 Java 编程中我们经常使用构造方法来初始化对象但实际上除了构造方法还有静态代码块static block和普通代码块初始化块这两种特殊的代码结构。它们也会在对象创建或类加载时执行但执行顺序和次数有着严格的规则。本文将结合具体示例详细讲解静态代码块、普通代码块与构造方法的执行机制帮助你彻底理清它们之间的关系。2. 静态代码块2.1 定义静态代码块使用static {}语法定义属于类级别与对象实例无关。classDemo{static{System.out.println(静态代码块);}}2.2 执行时机类加载时执行当 JVM 首次加载某个类时该类的静态代码块会被执行。只执行一次无论创建多少个该类的对象静态代码块在整个程序生命周期中只执行一次。静态代码块的执行早于main方法静态代码块在 JVM 调用main方法之前就已经执行完毕。2.3 多个静态代码块的顺序如果一个类中包含多个静态代码块它们会按照在代码中书写的顺序依次执行。classDemo{static{System.out.println(静态代码块 1);}static{System.out.println(静态代码块 2);}}// 输出静态代码块 1 → 静态代码块 23. 普通代码块构造代码块3.1 定义普通代码块直接使用{}定义没有static修饰符。它属于对象级别常被称为构造代码块或初始化块。classDemo{{System.out.println(普通代码块);}}3.2 执行时机对象创建时执行每创建一个该类的对象普通代码块都会执行一次。执行顺序在构造方法之前执行。编译后会被合并到构造方法中从字节码层面看普通代码块的内容会插入到每个构造方法的最前面在super()调用之后。3.3 与构造方法的区别特性普通代码块构造方法定义形式{}类名() {}是否可以有参数否是执行顺序先于构造方法后于普通代码块可重用性所有构造方法共享同一段初始化逻辑不同构造方法可以有不同实现为了更直观地理解上述执行顺序下面用一张流程图来描述静态代码块、普通代码块与构造方法在类加载与对象创建过程中的执行流程否是是否JVM 启动 / 首次使用类类是否已加载加载类执行静态变量赋值与静态代码块只一次跳过类加载阶段new 创建对象在堆内存中分配空间执行普通代码块初始化块执行构造方法体返回对象引用还要创建更多对象程序继续运行关键要点类加载阶段只发生一次在其中按书写顺序执行静态变量赋值和静态代码块之后每创建一个对象都会重复执行「普通代码块 → 构造方法」这一流程。4. 构造方法构造方法是对象创建的入口用于完成对象的初始化工作。每创建一个对象对应的构造方法就会被调用。当存在继承关系时子类构造方法的第一条语句通常为super()它会调用父类的构造方法。构造方法的执行顺序父类普通代码块 → 父类构造方法 → 子类普通代码块 → 子类构造方法假设父类已有静态代码块则父类静态代码块最先执行。5. 执行顺序验证下面通过一个完整的示例来验证静态代码块、普通代码块和构造方法的执行顺序。classDemo{publicDemo(){System.out.println(构造方法);}{System.out.println(代码块);}static{System.out.println(静态代码块);}}publicclassStaticAndVoidBlock{publicstaticvoidmain(String[]args){System.out.println(--- 第一次创建对象 ---);Demod1newDemo();System.out.println(--- 第二次创建对象 ---);Demod2newDemo();}}输出结果静态代码块 --- 第一次创建对象 --- 代码块 构造方法 --- 第二次创建对象 --- 代码块 构造方法结论静态代码块在程序开始运行、类加载时首先执行且只执行一次。每次创建对象时先执行普通代码块再执行构造方法。第二次创建对象时静态代码块不再执行但普通代码块和构造方法仍会重复执行。6. 深入理解类加载与对象创建的完整生命周期为了更清晰地理解上述机制可以将整个流程拆解为两个阶段6.1 类加载阶段加载JVM 在第一次主动使用某个类时从字节码中加载该类。链接验证、准备、解析。初始化执行静态变量赋值和静态代码块按书写顺序。此阶段线程安全且只执行一次。6.2 对象创建阶段new Demo()指令触发对象创建。在堆内存中分配空间。执行普通代码块初始化块。执行构造方法体。将对象引用赋给变量。由此可见静态代码块的static关键字使其提升到了类级别而普通代码块则是实例级别。7. 应用场景与注意事项7.1 静态代码块的典型用途加载静态资源如读取配置文件、加载数据库驱动、初始化静态集合等。单例模式的懒加载优化利用静态内部类实现线程安全的单例。执行一次性的前置检查例如检查系统属性、密钥等。classConfig{staticPropertiespropsnewProperties();static{try{props.load(Config.class.getResourceAsStream(/config.properties));}catch(IOExceptione){thrownewRuntimeException(e);}}}7.2 普通代码块的典型用途匿名内部类匿名内部类没有名字无法定义构造方法此时只能用普通代码块来初始化。简化构造方法当多个构造方法存在相同的初始化逻辑时可以将公共代码提取到普通代码块中避免重复。classBase{{System.out.println(公共初始化逻辑);}Base(){}Base(intx){}}7.3 注意事项不要滥用静态代码块过多的静态代码块会使类加载过程变得复杂且可能引发不易察觉的初始化顺序问题。避免在静态代码块中抛出未捕获的异常这可能导致类加载失败进而抛出ExceptionInInitializerError。子类与父类的顺序父类的静态代码块 → 子类的静态代码块 → 父类的普通代码块 → 父类的构造方法 → 子类的普通代码块 → 子类的构造方法。如果存在多级继承则按层级递推。与静态变量初始化的关系静态变量和静态代码块按书写顺序依次执行。classDemo{staticinta10;static{a20;}}// 最终 a 20因为静态代码块在后面执行覆盖了前面的赋值。8. 总结静态代码块类加载时执行只执行一次常用于静态资源的初始化。普通代码块每次创建对象时在构造方法之前执行可用于共享初始化逻辑。构造方法每次创建对象时执行负责对象的具体初始化可以有参数和重载。执行顺序静态代码块→普通代码块→构造方法同一对象创建周期内。掌握这三者的区别和联系不仅有助于理解 Java 对象的完整生命周期也是面试中常见的考点。希望本文通过代码示例和原理剖析能帮助你彻底理解并熟练运用它们。