Java本地缓存选型复盘:从HashMap到Caffeine,踩过的坑和留下的选择
一个Java后端在选型本地缓存时的实际对比,覆盖HashMap、Guava Cache、Caffeine和Ehcache的性能、功能与适用场景,没有标准答案,只有取舍。
支持通配符SSL证书、多域名证书、IP证书。适配ACME接口, 支持Zerossl、Let's Encrypt和Google等渠道。登录已有账号
2026-08-01 01:15:48 Java NoClassDefFoundError 类初始化 排错
线上突然冒出 java.lang.NoClassDefFoundError: Could not initialize class com.xxx.YourClass,而且堆栈里不会告诉你是哪一行初始化代码出的问题,只能看到类名。这种错误绝大多数情况都是这个类的静态代码块或者静态变量初始化的时候挂了。JVM 第一次主动用到一个类,会执行静态赋值和 static 块,一旦这个过程里抛了异常没兜住,JVM 就记下这个类初始化失败,之后要用就直接甩 NoClassDefFoundError。
静态初始化失败的场景很多。常见的就是在 static {} 里读配置文件、连数据库,或者给静态变量赋值时依赖了外部资源。下面这个写法很典型:
public class ExampleClass { private static String config; static { try { config = loadConfigFromFile("config.properties"); } catch (IOException e) { System.err.println("静态初始化失败: " + e.getMessage()); throw new ExceptionInInitializerError(e); } } private static String loadConfigFromFile(String path) throws IOException { if (!new File(path).exists()) { throw new IOException("配置文件未找到: " + path); } return "default_value"; } }
如果 config.properties 没在 classpath 能读到的位置,IOException 被 catch 后重新包装成 ExceptionInInitializerError,同样会终止类初始化。所以要么把文件放对地方,要么别让异常从静态块里跑出去。
还有人在静态块里直接调 Spring 的 ApplicationContext,结果容器还没起来就炸了,这种线上排查很费时间。
依赖类加载失败也是常有的。你的类本身没问题,但它引了另一个类,而那个类所在的 jar 没打进去,或者 scope 设成了 provided 但运行时实际需要。这时候赶紧看依赖树:Maven 用 mvn dependency:tree,Gradle 用 ./gradlew dependencies。如果已经拿到部署包,直接解压确认类文件在不在:
unzip your-app.jar -d extracted find extracted/WEB-INF/classes/ -name "YourClass.class"
Spring Boot 的 fat jar 会在 BOOT-INF/classes 下面,注意路径别找错。
类路径问题说白了就是编译时类在,运行时没了。Maven 打包漏了某个模块,或者手动部署少拷了 jar,都会这样。查 Maven 项目可以先看 target/classes 目录:
find target/classes/com/example/ -name "YourClass.class"
如果这里都没有,那很可能编译就没过。检查 pom.xml 里 maven-compiler-plugin 的 source/target 版本有没有写对,大致是:
Gradle 项目则在 build/classes/java/main 下找对应的类文件。
JAR 包冲突这个地方很多人绕进去。同一个类出现在不同版本的 jar 里,类加载器可能加载了不匹配的版本,导致方法找不到,初始化阶段就崩了。用 Maven 的 dependencyManagement 强制锁定版本比较稳:
Gradle 里可以直接排除冲突模块:
implementation('com.example:example-lib') { exclude group: 'com.conflict', module: 'conflict-lib' }
如果搞不清哪些版本在打架,就老老实实跑 mvn dependency:tree,把冲突的依赖 exclude 掉或者统一升、降版本。
清理缓存重新构建偶尔能救急。Maven 项目 mvn clean install -U,Gradle 项目 ./gradlew clean build --refresh-dependencies,强制刷新下 SNAPSHOT 依赖。私服缓存出问题的时候这一招挺管用。
预防方面,我们后来在项目里用 BOM 统一管理依赖版本,避免每人各引一套。
CI 流水线里加了个简单的类加载冒烟测试,容器启动后检查几个核心类能不能正常加载,不用等上线半天才发现初始化炸了。日志的话,如果用的容器或类加载器支持 verbose:class,可以临时打开看类加载过程,不过平时一般不动它,只有排查诡异问题才开。
最后得把 NoClassDefFoundError 和 ClassNotFoundException 分清,不少同事混用。
ClassNotFoundException 是你显式加载类(比如 Class.forName("xxx") 或者 ClassLoader.loadClass)时,类路径里完全找不到这个类的文件,是 Exception。NoClassDefFoundError 是 JVM 在隐式用到某个类时发现它的定义有问题(初始化失败或者类路径里没有这个类),是 Error。ClassNotFoundException 就是压根没有那个 class 文件;NoClassDefFoundError 是 class 文件在,但加载过程出了幺蛾子。所以遇到后者,重点查静态初始化、类路径和依赖冲突,不用去纠结类名是不是拼错了。
一个Java后端在选型本地缓存时的实际对比,覆盖HashMap、Guava Cache、Caffeine和Ehcache的性能、功能与适用场景,没有标准答案,只有取舍。
从一次线上报错说起,聊聊 Instant.parse 严格的格式要求、异常处理,以及和 LocalDateTime 的时区混用问题。
记录一个由于缺少闭合大括号导致 nginx -t 报语法错误的修复过程,涵盖常见原因和几步走的排查思路。
跑单测突然报 Command line is too long,大多数情况在 Run Configuration 里勾一个选项就完事。这里把原因和几种方案一起记一下。
面对Java解析XML,DOM与DOM4J是两大主流选择。本文详尽分析标准DOM的内置优势与DOM4J在API简洁性、性能及XPath支持上的卓越表现。无论小型配置还是大型数据处理,助你理解两者差异,做出明智的技术选型。