知其然,知其所以然
Java 内部原理 2026-10-05

Maven 生命周期:阶段与插件目标如何组合

用 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 不再执行。

Java Maven 构建 生命周期 插件目标 真实验证 动画演示
Java 内部原理 2026-10-05

Gradle 增量构建:任务输入输出决定缓存

用 Gradle 9.8.0 在 JDK 21 上实测增量构建:没有改动时 14 个任务 UP-TO-DATE、只 touch 源文件仍然 UP-TO-DATE;改 private 方法体 app 不重新编译、新增 public 方法才重新编译;没声明为输入的文件改了产物就过期;40 个类里只增量编译 2 到 4 个;clean 之后构建缓存取回 2 个编译任务。

Java Gradle 构建 增量构建 构建缓存 真实验证 动画演示
Java 内部原理 2026-10-05

Java API 变更报告

用 japicmp 0.23.1 生成 Java API 变更报告,并把预测与真实运行对照:14 个场景里二进制预测与旧 client 的运行结果一致 13 个(接口新增抽象方法被报告为二进制兼容,实际抛 AbstractMethodError),源码预测与 javac 一致 12 个;枚举顺序与常量值的变化没有任何报告;commons-lang3 3.12.0 → 3.18.0 没有二进制不兼容。

Java API 二进制兼容 japicmp 语义化版本 真实验证 动画演示
Java 内部原理 2026-10-05

Java 依赖锁定

用本地 file:// 仓库与 Gradle 9.8.0、Maven 3.10.0 实测依赖锁定:不锁时动态版本在发布新版本后漂移(包括传递依赖),gradle.lockfile 把 lib 与传递依赖 core 都锁住,--update-locks 可以只更新指定依赖;三种 lockMode 的差别;锁里的版本会压过声明的范围;校验文件能发现被篡改的 jar。

Java 依赖管理 依赖锁定 Gradle Maven 真实验证 动画演示
Java 内部原理 2026-10-05

Java 发布产物签名

用 keytool / jarsigner(JDK 21)与 BouncyCastle bcpg 1.78.1 实测发布产物签名:篡改已签名的类在 jarsigner -verify 与运行时都抛 SecurityException,同样的篡改在未签名的 jar 上没有任何报错;攻击者用自己的证书重新签名同样 verified;SHA1 签名被当作未签名;校验和与 PGP 签名的差别;签名后加载 1000 个类慢约 1.19 倍。

Java 发布产物 签名 jarsigner PGP 真实验证 动画演示
Java 内部原理 2026-10-04

NUMA 与 Java 进程

在没有 NUMA 的 macOS 机器上能验证的部分:JDK 21 与 27 的五种收集器都接受 -XX:+UseNUMA 但日志统一是 NUMA Support: Disabled;UseNUMA 相关参数的默认值;-XX:ActiveProcessorCount 如何改变 CPU 数、commonPool 与 G1 线程数。NUMA 的真实收益需要在 Linux 多路机器上验证。

Java NUMA UseNUMA ActiveProcessorCount 垃圾回收 诚实的限制 动画演示
Java 内部原理 2026-10-04

JDBC Driver SPI:驱动如何被发现和加载

用 H2、HSQLDB、sqlite-jdbc、Derby 四个真实驱动验证 JDBC Driver SPI:META-INF/services/java.sql.Driver 让 DriverManager 自动发现驱动(20 ms,不需要 Class.forName);按 acceptsURL 与注册顺序选择驱动;注销与重新注册;其它类加载器加载的驱动不会被调用者使用;自己的驱动通过 Class.forName、-Djdbc.drivers 或服务文件被注册。

Java JDBC Driver SPI ServiceLoader DriverManager 真实验证 动画演示
Java 内部原理 2026-10-04

JDBC 批量更新:减少往返但控制批次大小

用自己起的 PostgreSQL 16 与 MySQL 实例真实对比 JDBC 批量插入 20000 行:PostgreSQL 逐行 932 ms、批 100 约 60 ms、reWriteBatchedInserts 后 21–31 ms;MySQL 不加 rewriteBatchedStatements 时批处理没有提速(约 650 ms),加了之后 1000 行一批 67.5 ms;批里有一行失败时两个数据库的 getUpdateCounts 语义不同。

Java JDBC 批量更新 addBatch rewriteBatchedStatements reWriteBatchedInserts 真实验证 动画演示
Java 内部原理 2026-10-04

JDBC 事务隔离级别

用自己起的 PostgreSQL 16 与 MySQL 实例,在 JDBC 里对四个隔离级别逐个构造脏读、不可重复读、幻读、丢失更新、写偏斜:PostgreSQL 的 READ_UNCOMMITTED 等同 READ_COMMITTED、REPEATABLE_READ 拒绝丢失更新但仍有写偏斜、SERIALIZABLE 拒绝写偏斜;MySQL 的 REPEATABLE_READ 下丢失更新与写偏斜都发生,SERIALIZABLE 靠加锁导致阻塞与死锁。

Java JDBC 事务隔离级别 PostgreSQL MySQL 写偏斜 真实验证 动画演示
Java 内部原理 2026-10-04

JDBC Savepoint:在一个事务中局部回滚

用自己起的 PostgreSQL 16 与 MySQL 实例验证 JDBC 保存点:PostgreSQL 语句出错后事务中止,commit 静默变回滚,保存点可以恢复;MySQL 语句失败不会中止事务;保存点的嵌套、失效、命名规则与错误码;pgjdbc 的 autosave=always 让 20000 行逐行插入从 564.9 ms 变成 1111.3 ms。

Java JDBC Savepoint 保存点 事务 autosave 真实验证 动画演示
Java 内部原理 2026-10-04

JDBC 游标与 Fetch Size

用自己起的 PostgreSQL 16 与 MySQL 读 200 万行验证 JDBC fetchSize:PostgreSQL 必须 autocommit=false 才用游标,否则 -Xmx128m 下 OutOfMemoryError,fetchSize=10 慢到 4899 ms;MySQL 默认与只设 fetchSize 都 OOM,要用 Integer.MIN_VALUE 流式(577 ms)或 useCursorFetch(1561 ms)。

Java JDBC fetchSize 游标 OutOfMemoryError PostgreSQL MySQL 真实验证 动画演示
Java 内部原理 2026-10-04

JDBC Generated Keys:安全取得数据库生成主键

在 PostgreSQL 与 MySQL 上验证 JDBC 取生成主键:PostgreSQL 的 RETURN_GENERATED_KEYS 返回整行(RETURNING *),指定列名才只返回 id,不支持列序号;MySQL 永远只有 GENERATED_KEY 一列,没请求就报错,rewriteBatchedStatements 让批的更新计数变成 -2;并发下 select max(id) 有 2919 / 3200 次与自己的键不一致。

Java JDBC GeneratedKeys 自增主键 PostgreSQL MySQL 真实验证 动画演示
京ICP备2026067502号-1京公网安备11010502063487号