静态代码块不支持按需加载,因其在类初始化阶段强制执行,属饿汉式;真正实现按需加载的是静态内部类模式,它利用jvm类加载机制,在首次访问holder.instance时才初始化,线程安全且零锁。

静态代码块本身不支持按需加载,它是“饿汉式”的——类一初始化就执行,无法延迟。所谓“按需”,必须借助静态内部类机制来实现。
为什么 static {} 不能按需加载
静态代码块在 JVM 类初始化阶段触发,时机由类首次主动使用决定(如访问静态字段、调用静态方法)。一旦触发,立即执行,且不可中断或推迟。它没有判断条件,也不响应业务调用时机,因此天然不适合“按需”场景。
真正可行的按需加载方案:静态内部类模式
这是 Java 中最经典、JVM 原生保障的懒加载方式,线程安全、异常隔离、无额外开销:
- 定义一个 static class Holder,把耗时初始化逻辑(如读取大配置、构建缓存、加载证书)放在其静态代码块中
- 对外暴露一个 public static final Resource INSTANCE = Holder.INSTANCE
- Holder 类只有在首次访问 INSTANCE 时才被初始化,JVM 保证该过程仅一次、线程安全、失败不影响外层类
典型代码结构
以加载 config.properties 并解析为 Map 为例:
public class AppConfig {
public static final Map<string string> CONFIG = Holder.INSTANCE;
private static class Holder {
private static final Map<string string> INSTANCE;
static {
Properties props = new Properties();
try (InputStream is = AppConfig.class.getResourceAsStream("config.properties")) {
if (is == null) {
throw new IllegalStateException("config.properties not found in classpath");
}
props.load(new InputStreamReader(is, "UTF-8"));
INSTANCE = props.stringPropertyNames().stream()
.collect(Collectors.toMap(k -> k, props::getProperty));
} catch (Exception e) {
throw new ExceptionInInitializerError("Failed to load config", e);
}
}
}
}</string></string>
什么情况该用这种模式
适合以下资源:启动时不急需、初始化开销大、或依赖尚未就绪的外部环境:
- 大型本地字典缓存(如城市编码表、错误码映射)
- 预编译的复杂正则 Pattern 或 JSON 解析器(ObjectMapper)
- 需要从文件系统或系统属性读取路径后再加载的资源
- 本地 native 库(System.loadLibrary),避免类加载卡死
注意避开两个误区
一是别在静态块里做 HTTP 请求、数据库连接或远程配置拉取——这些操作不可控、易超时、会阻塞整个类初始化;二是别误以为热部署(如 DevTools)下能“重新触发”静态块——新类加载器加载的是全新类,旧值不会延续,这不是按需,而是重复加载。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











