静态代码块适合预加载类加载时必须就绪、值稳定且无运行时依赖的资源,如小型配置文件、不可变常量、jdbc驱动、正则pattern、本地缓存等;需声明为public static final,用getresourceasstream安全读取,异常转exceptionininitializererror。

静态代码块是 Java 类加载初始化阶段执行一次的天然入口,适合做轻量、确定、无外部依赖的资源预加载——它不靠 Spring、不等 main 启动、线程安全,但写错一行就可能让整个类不可用。
哪些资源适合放进静态块预加载
不是所有“想提前加载”的东西都该放这里。核心判断标准是:是否满足「类一加载就必须就绪、值稳定、无运行时上下文依赖」。
- 从 classpath 加载的小型配置文件(如 app.properties),且路径确定、编码可控
- 不可变的全局常量,比如数据库类型码表 Map.of("mysql", 1, "pg", 2)
- JDBC 驱动类显式加载(Class.forName("...Driver")),作为 SPI 自动注册的兜底
- 预编译的正则 Pattern、共享的 JSON 解析器(ObjectMapper)单例
- 本地缓存的初始键值对(如错误码映射、地区字典),不涉及远程拉取或持久化写入
怎么写才真正安全可靠
光有 static{} 不够,关键在容错、顺序和不可变性。
- 所有静态字段声明为 public static final,防止后续被意外修改
- 用 getResourceAsStream() 读配置,避免硬编码路径;优先 classpath,再 fallback 到系统属性指定路径
- IO 操作必须 try-with-resources + 显式 catch,异常转为 ExceptionInInitializerError 并附带清晰提示
- 字符串类配置调用 props.getProperty(key, defaultValue),避免 null 导致 NPE
- 多个静态变量与块按源码顺序执行,把基础配置(如路径、编码)放在前面,依赖它的逻辑放后面
常见踩坑点和替代建议
很多问题不是静态块不行,而是用错了场景。
- 不要在静态块里创建数据库连接池或发起 HTTP 请求——网络耗时会卡住类加载,失败即 NoClassDefFoundError
- 不要读取未打包进 jar 的外部文件(如 /etc/app/config.yml),除非你已通过 -Dconfig.path=... 显式传参并做了空检查
- 避免调用尚未初始化的日志框架方法(如 SLF4J 的 LOGGER.info()),可用 System.out.println() 做启动期简单输出
- 如果配置来源复杂(如 Nacos、Apollo)、需重试或监听变更,应改用懒加载(Double-Check Locking + volatile)或容器托管(如 Spring @PostConstruct)
一个生产可用的模板示例
以下代码兼顾可读性、容错性和 JDK 8+ 兼容性:
public class GlobalConfig {public static final String API_BASE_URL;
public static final int TIMEOUT_MS;
private static final Properties props = new Properties();
static {
InputStream is = null;
try {
is = GlobalConfig.class.getClassLoader().getResourceAsStream("api-config.properties");
if (is == null) {
throw new IllegalArgumentException("api-config.properties not found in classpath");
}
props.load(is);
API_BASE_URL = props.getProperty("api.base.url", "https://api.example.com");
TIMEOUT_MS = Integer.parseInt(props.getProperty("api.timeout.ms", "5000"));
System.out.println("[GlobalConfig] loaded: " + API_BASE_URL);
} catch (Exception e) {
throw new ExceptionInInitializerError("Failed to init GlobalConfig", e);
} finally {
if (is != null) try { is.close(); } catch (IOException ignored) {}
}
}
}
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











