用 JUnit 5.11.3 实测并行测试:16 个 300 ms 的 I/O 测试从串行 4966 ms 降到 fixed 并行度 16 的 360 ms;只有类并发时类内方法仍串行(1288 ms);CPU 型测试在 10 核机器上并行度 10 → 16 只有 834 → 795 ms;@ResourceLock 同一个键让 16 个测试变回完全串行(5005 ms);@Isolated 与 @Execution(SAME_THREAD) 的影响。
实测不稳定测试:单例竞争在空闲时失败 9.63%、满载时只有 0.45%,时序断言空闲 0%、满载 72.67%,顺序依赖 16.15% 与负载无关;最多重试 3 次把 167 次真实竞争失败中的 166 次变成“通过”;JUnit 重试扩展里 aborted 的次数就是被掩盖的失败;@Tag("quarantine") 把不稳定测试从门禁里拿出来。
用 JaCoCo 0.8.12 与 PIT 1.17.1 在同一份代码上实测覆盖率的误区:没有任何断言的测试拿到行覆盖 88%、分支覆盖 100%、变异得分 0%;带分支与边界断言的测试同样 88% / 100%,变异得分 100%;一行代码 100% 行覆盖时分支覆盖可以只有 2 / 4;JaCoCo 探针只让 CPU 型循环慢 1.03 倍。
在 JDK 17 / 21 / 27 上实测时区与 Locale 对测试的影响:user.language 在启动后改变不起作用而 Locale.setDefault 立即生效("title".toUpperCase() 在 tr-TR 下变成 TİTLE);缓存的 Clock 不会跟随新的默认时区;夏令时当天有 23 或 25 小时;JDK 17 与 21 的本地化输出有 1 / 7 行不同。
用 Maven 3.10.0 在 JDK 21 上实测生命周期:30 个合法阶段,jar 打包下 compile / test / package / install 分别执行 2 / 5 / 6 / 7 个目标;同一阶段内默认绑定先于 pom 里按声明顺序的 execution;直接调用 jar:jar 在没有编译时得到 0 个 .class 的空 jar;-DskipTests 与 -Dmaven.test.skip=true 的区别;测试失败时 jar:jar 不再执行。
用 Gradle 9.8.0 在 JDK 21 上实测增量构建:没有改动时 14 个任务 UP-TO-DATE、只 touch 源文件仍然 UP-TO-DATE;改 private 方法体 app 不重新编译、新增 public 方法才重新编译;没声明为输入的文件改了产物就过期;40 个类里只增量编译 2 到 4 个;clean 之后构建缓存取回 2 个编译任务。
用 japicmp 0.23.1 生成 Java API 变更报告,并把预测与真实运行对照:14 个场景里二进制预测与旧 client 的运行结果一致 13 个(接口新增抽象方法被报告为二进制兼容,实际抛 AbstractMethodError),源码预测与 javac 一致 12 个;枚举顺序与常量值的变化没有任何报告;commons-lang3 3.12.0 → 3.18.0 没有二进制不兼容。
用本地 file:// 仓库与 Gradle 9.8.0、Maven 3.10.0 实测依赖锁定:不锁时动态版本在发布新版本后漂移(包括传递依赖),gradle.lockfile 把 lib 与传递依赖 core 都锁住,--update-locks 可以只更新指定依赖;三种 lockMode 的差别;锁里的版本会压过声明的范围;校验文件能发现被篡改的 jar。
用 keytool / jarsigner(JDK 21)与 BouncyCastle bcpg 1.78.1 实测发布产物签名:篡改已签名的类在 jarsigner -verify 与运行时都抛 SecurityException,同样的篡改在未签名的 jar 上没有任何报错;攻击者用自己的证书重新签名同样 verified;SHA1 签名被当作未签名;校验和与 PGP 签名的差别;签名后加载 1000 个类慢约 1.19 倍。
在没有 NUMA 的 macOS 机器上能验证的部分:JDK 21 与 27 的五种收集器都接受 -XX:+UseNUMA 但日志统一是 NUMA Support: Disabled;UseNUMA 相关参数的默认值;-XX:ActiveProcessorCount 如何改变 CPU 数、commonPool 与 G1 线程数。NUMA 的真实收益需要在 Linux 多路机器上验证。
用 H2、HSQLDB、sqlite-jdbc、Derby 四个真实驱动验证 JDBC Driver SPI:META-INF/services/java.sql.Driver 让 DriverManager 自动发现驱动(20 ms,不需要 Class.forName);按 acceptsURL 与注册顺序选择驱动;注销与重新注册;其它类加载器加载的驱动不会被调用者使用;自己的驱动通过 Class.forName、-Djdbc.drivers 或服务文件被注册。
用自己起的 PostgreSQL 16 与 MySQL 实例真实对比 JDBC 批量插入 20000 行:PostgreSQL 逐行 932 ms、批 100 约 60 ms、reWriteBatchedInserts 后 21–31 ms;MySQL 不加 rewriteBatchedStatements 时批处理没有提速(约 650 ms),加了之后 1000 行一批 67.5 ms;批里有一行失败时两个数据库的 getUpdateCounts 语义不同。