知其然,知其所以然
Java 内部原理 2026-06-20

Fork/Join 真实分给了几个线程:真实验证 4 个工作线程真的都参与了计算

用 RecursiveTask 实现一个分治求和,限定 ForkJoinPool 只有 4 个工作线程,真实跑 2000 万个元素的数组求和:真实记录到 4 个不同的 ForkJoinPool 工作线程都参与了计算,和线程池的并行度设置精确对应;真实结果和单线程顺序求和完全一致;但真实计时发现,对这种每个元素只做一次加法的极轻量任务,Fork/Join 版本反而比顺序循环慢了近一倍——任务切分和线程调度的真实开销,在计算量太小时会超过并行带来的收益。

Java Fork/Join 并发 动画演示
Java 内部原理 2026-06-19

BigDecimal 精度陷阱:真实验证 new BigDecimal(0.1) 会原样继承 double 的不精确

真实验证 0.1 + 0.2 用 double 计算真实等于 0.30000000000000004,不等于 0.3;打印 0.1 在 double 里真实存储的完整精度,发现它本来就不是精确的 0.1;真实验证 new BigDecimal(0.1) 会原样把这份不精确继承过来,而 BigDecimal.valueOf(0.1) 和 new BigDecimal 字符串版本真实都得到精确的 0.1。额外真实验证 BigDecimal 的 equals 对小数位数敏感,compareTo 才是真正的数值比较。

Java BigDecimal 浮点数 动画演示
Java 内部原理 2026-06-18

可变参数遇上重载:真实验证参数个数对得上时,编译器真实优先选固定版本

同时定义 pick(int a, int b) 和 pick(int... nums),真实调用 pick(1, 2):编译器真实选中固定两参数版本,不是可变参数版本;调用 pick(1, 2, 3)、pick(1)、pick() 这些参数个数对不上固定版本的场景,才真实退而选中可变参数版本。真实验证类型匹配上也遵循同样优先级:pick2(String) 和 pick2(Object...) 同时存在时,pick2(hello) 真实选中固定 String 版本。

Java 可变参数 重载 动画演示
Java 内部原理 2026-06-17

Optional 本身可以是 null:真实验证这个类型没能真正杜绝 NPE

真实验证空 Optional 调用 get() 真实抛出 NoSuchElementException;更关键的是,写一个返回类型是 Optional<String> 但内部直接 return null 的方法,真实验证编译器完全不会阻止这一行,调用方拿到的返回值真实是 null,对它调用 isPresent() 真实抛出 NullPointerException——Optional 类型本身没有任何语言层面的机制阻止它自己变成 null。对照 orElse/ifPresentOrElse 等正确用法真实不需要显式判空。

Java Optional NPE 动画演示
Java 内部原理 2026-06-16

thenApply 真实可能在调用线程执行,不一定是线程池:真实测出两种结果

真实验证 supplyAsync 默认在 ForkJoinPool.commonPool 的工作线程执行,和 main 线程真实不同;但紧跟着的 thenApply(不带 Async 后缀)在 supplyAsync 几乎瞬间完成的情况下,真实在 main 线程执行,不是线程池;把 supplyAsync 真实故意拖慢到 200ms,同一个 thenApply 这次真实变成在线程池工作线程执行。真实证明:不带 Async 后缀的回调,由谁执行取决于回调附加时前一步是否已经完成,不是固定跑在线程池里。

Java CompletableFuture 并发 动画演示
Java 内部原理 2026-06-15

Record 类真实反编译:equals/hashCode/toString 是一条 invokedynamic,不是手写代码

定义 record Point(int x, int y),真实验证 equals() 按字段值比较、hashCode() 相应相等、toString() 自动生成 Point[x=3, y=4] 格式、访问器方法名就是字段名本身;真实验证紧凑构造函数里的校验逻辑会执行。用 javap 反编译发现 equals/hashCode/toString 在字节码里各自只是一条 invokedynamic 指令,真正的比较逻辑由 JDK 内部的 ObjectMethods 引导方法在运行时生成,不是编译器手写进类文件里的。

Java Record Java 16+ 动画演示
Java 内部原理 2026-06-14

Comparator 违反契约:真实触发 JDK 自带的 Comparison method violates its general contract

写一个看起来合理、实际违反传递性的比较器(差值小于 2 就判定相等),先用三个数真实证明 compare(10,11)=0、compare(11,12)=0,但 compare(10,12)=-2,传递性真实被破坏;换成完全随机的比较器,数组长度 64,真实在第 6 次尝试时触发 IllegalArgumentException;对照数组长度 20(小于 TimSort 的 MIN_MERGE=32 阈值),跑 2000 次同样的随机比较器,真实一次都没有触发。

Java Comparator TimSort 动画演示
Java 内部原理 2026-06-13

三元表达式里的拆箱 NPE:真实验证条件选中的分支才会真实拆箱

流传很广的说法是 flag ? 0 : nullableInteger 这种写法,哪怕 flag 为 true 走的是 0 分支,也会因为编译器要统一类型而提前拆箱 nullableInteger,导致真实抛出 NullPointerException——真实反编译字节码发现在 Java 17 上这个说法不成立:只有真正选中 null 分支时才会调用 Integer.intValue()。真实测出 flag=true 时正常返回 0,flag=false 时才真实抛出 NPE。

Java 自动拆箱 NPE 动画演示
Java 内部原理 2026-06-12

String.hashCode() 真实公式:真实造出一对内容完全不同却哈希相同的字符串

真实验证 Aa 和 BB 这对经典碰撞:两者 hashCode() 真实都是 2112,内容完全不同,equals() 真实是 false;按 s[0]*31+s[1] 公式手动真实计算,结果和 String.hashCode() 完全一致;给两个字符串加上同样的前缀 hello-,两者的 hashCode 真实依然相等,验证了碰撞可以递归扩展到任意长度;最后把这对碰撞的 key 放进 HashMap,真实验证两个 key 尽管哈希相同,依然被正确当成两个独立条目。

Java hashCode String 动画演示
Java 内部原理 2026-06-11

方法重载 vs 重写:真实验证重载看编译期类型,重写看运行时类型

声明 Animal a = new Dog(),真实调用 a.sound()(重写方法),结果按运行时真实类型 Dog 分派,输出汪汪;真实调用重载方法 describe(a),编译期声明类型是 Animal,即使运行时真实是 Dog,重载解析依然真实选中了 describe(Animal) 版本,不是 describe(Dog);把同一个对象强转成 Dog 类型再传进去,重载解析结果真实跟着变了。重载在编译期就真实决定调用哪个版本,重写在运行时才真实决定。

Java 重载重写 语言特性 动画演示
Java 内部原理 2026-06-10

类到底什么时候被初始化:真实验证编译期常量根本不会触发它

一个类里有 static final int CONST = 42,另一个类里有 static final int NON_CONST 但要调用方法才能算出来;真实运行发现:读取 HasConstant.CONST 之前,它的静态初始化块真实完全没有执行;读取 HasNonConstant.NON_CONST 时,静态初始化块真实执行了。用 javap 反编译发现更彻底的真相:引用 HasConstant.CONST 的地方,编译器直接把字面量 42 内联进了字符串常量池,连一次真实的 getstatic 字节码都没有生成。

Java 类初始化 JVM 动画演示
Java 内部原理 2026-06-09

Thread.interrupt() 真实语义:忙等循环真的不会被打断

对一个死循环忙等的线程调用 interrupt(),真实验证它完全不会停下来:isInterrupted() 变成 true,但循环计数器真实还在持续增长;对照一个阻塞在 Thread.sleep(60000) 里的线程,interrupt() 真实立刻让它抛出 InterruptedException 提前醒来,总耗时约 205ms,远小于计划的 60 秒;第三组验证协作式取消:循环里主动检查 isInterrupted(),真实能在 interrupt() 之后很快退出循环。interrupt() 的真实语义只是设置一个标志位,不是强制终止。

Java 线程中断 并发 动画演示
京ICP备2026067502号-1京公网安备11010502063487号