静态代码块适合一次性初始化,但需谨慎使用:只放编译期不确定却运行初期必需的常量、轻量单例、类级钩子或简单校验;避免大对象、io、网络等耗时操作;注意执行顺序、异常兜底、线程安全及类加载器边界。

静态代码块确实适合做一次性初始化,但效率高低不取决于“用了没用”,而在于怎么写、放什么、是否踩了加载期的隐性陷阱。核心是让类加载快、稳、可预期。
只放真正需要“类加载时就到位”的东西
不是所有静态初始化都该塞进 static {}。优先考虑以下几类:
- 编译期无法确定、但运行初期就必须存在的常量(如从配置文件读取的 DB_URL)
- 轻量级单例工具(如预热的 ObjectMapper、Logger、Pattern)
- 注册类级钩子(如 Runtime.addShutdownHook)或监控指标
- 简单计算或校验(如检查系统属性合法性、设置默认值)
避免放入:大对象实例、网络请求、文件扫描、数据库连接池启动——这些应交给 Spring 的 @PostConstruct 或显式初始化方法。
控制执行顺序,避免隐式依赖失败
静态变量赋值和静态代码块按源码顺序混合执行。若后面块依赖前面变量,而前面变量初始化出错(比如抛异常),整个初始化就中断。
- 把强依赖项(如配置加载)写在最前面
- 静态字段尽量声明为 public static final,既语义清晰,又防止后续误改
- 不要在块中访问尚未声明的静态字段——即使编译通过,也可能拿到默认值(0、null)
异常必须兜底,否则类直接“报废”
static {} 里任何未捕获的异常都会包装成 ExceptionInInitializerError,导致该类后续所有使用都抛 NoClassDefFoundError。
- 所有 IO、解析、转换操作必须用 try-catch 包裹
- catch 后建议 throw new ExceptionInInitializerError("明确描述", e)
- 不要静默吞掉异常,也不要用 System.out.println 替代日志
别碰线程不安全资源,也别假定“已就绪”
JVM 保证 static {} 执行本身是线程安全的,但里面创建的对象未必安全:
- 如果初始化了一个 HashMap,后续多线程并发读写仍需同步(可用 ConcurrentHashMap 替代)
- 不要在块里 new 当前类的实例——可能触发循环初始化,JVM 直接报错
- 不能访问 this、不能调用非静态方法、不能初始化非静态字段(编译不通过)
留意类加载器边界,避免“看似重复执行”
static {} 确实只对同一个 ClassLoader 执行一次。但在开发阶段容易误判:
- Spring Boot 热部署会创建新 WebAppClassLoader,旧类卸载、新类重载 → 静态块再次执行
- 单元测试中反复加载同一类(尤其用 Class.forName("X", true, cl))也会触发多次
- 这不是 bug,是机制使然;调试时可通过打印 ClassLoader 哈希值确认是否真重复
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











