《Java 高级编程》前言

交代本书的读者对象、前置知识与九章结构,列出 JDK 25、Maven、Docker 与 clang 的环境清单和真实版本输出,说明代码、实验与命令的书写约定、三种验证层次,以及版本不一致时从 JDK、预览开关、模块路径到本地库的六步排查顺序。

这本书写给谁

本书写给已经能写 Java,但对「运行时到底发生了什么」缺少系统认识的开发者。典型情况有三类。

第一类是做业务开发两三年,能熟练使用集合、Stream、Spring 注解,但遇到 ClassNotFoundException、NoSuchMethodError、IllegalMonitorStateException 时只能搜索报错。第二类是从其他语言转来,知道协程、泛型、模块,但不确定 Java 的对应物在哪个版本、有什么限制。第三类是要做技术选型或面试准备,需要把零散的知识点连成一条可解释的链路。

不适合两类读者:完全没写过 Java 的初学者,以及只想找「XX 面试题标准答案」的读者。前者请先读卷一《Java 编程入门》,后者会发现本书的结论都带条件和版本。

前置知识

假定你已经掌握:

  • Java 基本语法、类与接口、异常、集合、Stream、Lambda。
  • 多线程的基本概念:Thread、Runnable、ExecutorService、synchronized 的粗略用法。
  • Maven 的基本命令:mvn compile、mvn test、mvn package。
  • 能读懂 record、sealed、switch 表达式;如果读不懂,第 7 章会补,但前面几章会用到 record。

不要求你读过卷二,但本书的例子大量引用 libraryd 的类型。第一次出现时会给出定义,完整的模块结构见下一节。

环境清单

本书的代码在下面这套环境里编译和运行。版本不是「建议」,而是文中输出的来源;版本不同,输出可能不同。

$ java -version
openjdk version "25.0.5" 2026-10-20 LTS
OpenJDK Runtime Environment Temurin-25.0.5+8 (build 25.0.5+8-LTS)
OpenJDK 64-Bit Server VM Temurin-25.0.5+8 (build 25.0.5+8-LTS, mixed mode, sharing)

$ javac -version
javac 25.0.5

$ mvn -version
Apache Maven 3.9.12 (848fbb4bf2d427b72bdb2471c22fced7ebd9a7a1)
Maven home: /Users/bingrong.yan/apache-maven-3.9.12
Java version: 25.0.5, vendor: Eclipse Adoptium, runtime: /Users/bingrong.yan/.jdks/jdk-25.0.5+8/Contents/Home

$ docker --version
Docker version 29.7.2, build a7dcaa6fdb

$ clang --version
Apple clang version 16.0.0 (clang-1600.0.26.6)
Target: arm64-apple-darwin25.3.0

机器是 Apple M1 Pro,macOS 26.3。第 9 章的 JMH 数字来自这台机器,换机器后数值会变,但相对关系通常保留。

JDK 路径按你的安装位置调整。本书统一用:

export JAVA_HOME=~/.jdks/jdk-25.0.5+8/Contents/Home
export PATH="$JAVA_HOME/bin:$PATH"

如果 java -version 显示的不是 25,先确认 JAVA_HOME,再看 mvn -version 里的 runtime。Maven 会用 JAVA_HOME 指向的 JDK,而不是 PATH 里第一个 java。

项目结构

卷三继续使用卷二的 libraryd,保留五个模块,新增两个只在开发和验证时使用的模块:

libraryd/
├── pom.xml                       # com.plume:libraryd-parent:1.0.0-SNAPSHOT
├── libraryd-domain/              # 领域类型、泛型仓储、sealed 结果、模式匹配
├── libraryd-persistence/         # JDBC 仓储、动态代理事务、并发事件流水线
├── libraryd-web/                 # Javalin 7、虚拟线程、结构化并发聚合
├── libraryd-app/                 # AppMain、装配、module-info、FFM 调用
├── libraryd-integration-tests/   # Testcontainers 集成测试
├── libraryd-agent/               # 第 3 章新增:Premain-Class 计时 Agent
└── libraryd-benchmarks/          # 第 9 章新增:JMH,不随镜像发布

依赖方向不变:domain 不依赖任何模块;persistence、web 依赖 domain;app 依赖全部;integration-tests 依赖 app。agent 只依赖 Byte Buddy 和 JDK,benchmarks 依赖 domain 与 persistence。

包根是 com.plume.library。本书不会为了演示某个机制而改包名,也不会新建一套平行的示例工程;所有代码都放回 libraryd 的对应模块里。

全书结构

九章分四个部分,每章三节:

部分章主题落点
一、类型1类型系统进阶Repository<T, ID>、型变、类型令牌
2反射、注解与动态代理反射工具、@Audit、事务代理
3类加载、字节码与 Instrumentation插件加载器、javap、计时 Agent
二、并发4JMM 与并发原语可见性、原子类、锁
5JUC 与并发容器并发容器、事件流水线、同步器
6虚拟线程与结构化并发虚拟线程、StructuredTaskScope、ScopedValue
三、语言与平台7现代语法与模式匹配LoanState、穷尽 switch、守卫
8JPMS、FFM 与原生互操作module-info、jlink、本地库调用
四、性能9性能工程JMH、JFR、优化决策

第 1–3 章是「编译期与运行时能看到什么」,第 4–6 章是「多个线程同时做一件事时规则是什么」,第 7 章把前两类问题用类型表达出来,第 8 章处理 Java 代码与外部世界的边界,第 9 章处理「怎么知道改动真的有效」。

代码与输出约定

完整类。 能独立编译的类会给完整源码,包括 package、import 和 main(如果可运行)。你可以直接复制到文件里执行。

片段。 只展示关键部分的代码用省略号或注释说明上下文,正文会写清「这段代码位于哪个方法里」。片段不保证单独编译。

反例。 标注「反例」的代码块故意保留错误,可能编译失败或抛异常。它们用来展示边界,不要直接用于生产。

真实输出。 放在 text 块里,并标注「本机输出」。如果为了排版删除了无关行,会写明「已省略」。没有真实运行过的输出标为「示意输出」。

命令。 bash 块里的命令按顺序执行。涉及端口的命令会给出可替换的端口号;涉及临时目录的用 /tmp/vol3-*,不污染仓库。

版本标注。 涉及预览 API 的代码块会写 --enable-preview;涉及本地访问的会写 --enable-native-access。如果某个结论只适用于特定 JDK 版本,正文会直接写出范围。

三种验证层次

本书的代码按可验证程度分三层,每节会尽量做到前两层。

第一层:编译。 所有完整类在 JDK 25 下通过 javac --release 25。用到预览 API 的类通过 javac --release 25 --enable-preview。读者可以用同一条命令复现。

第二层:运行。 能独立运行的程序会给出 java 命令和真实输出。并发、类加载、Agent、FFM、JMH、JFR 的实验都属于这一层。运行结果依赖机器和调度,正文会写清哪些是确定性输出,哪些是本次运行的观测值。

第三层:项目集成。 涉及 libraryd 多模块的改动,用 mvn -q -DskipTests package 验证编译与打包;涉及数据库的用 Testcontainers 或本机 PostgreSQL。第三层不会在每节都跑,但每章至少有一个可运行的落地点。

版本不一致时怎么排查

如果你在自己的环境里得到不同结果,按下面顺序排查,不要先改代码。

  1. JDK 版本。 java -version 与 javac -version 是否都是 25。--release 25 只能保证字节码目标,不能替代运行时的 JDK 25。
  2. 预览开关。 StructuredTaskScope、primitive patterns 需要 --enable-preview,编译和运行都要加。只加编译不加运行会得到 UnsupportedClassVersionError 或预览特性不匹配的报错。
  3. 模块路径。 第 8 章的 jlink、module-info 对 --module-path 敏感。类路径和模块路径混用时,先看 jdeps 的输出。
  4. 本地库。 FFM 示例需要 libisbn.dylib(macOS)或 libisbn.so(Linux)在 java.library.path 或显式路径下。找不到库时 SymbolLookup 返回空,不要误判为 API 用错。
  5. 并发调度。 可见性和重排序实验可能在某些运行中「看起来正常」。先确认线程数、迭代次数和 -XX 参数与正文一致,再判断结论。
  6. 依赖版本。 JMH、Byte Buddy、ASM 的版本写在父 POM 的 dependencyManagement 里。子模块不要单独写版本,否则会出现「代码一样、行为不同」。

关于示例的取舍

本书刻意不引入 Lombok、MapStruct、Guava 等简化库。原因不是它们不好,而是它们会遮住本书要讲的机制:Lombok 用注解处理器生成代码,MapStruct 用注解处理器生成映射,Guava 的缓存和并发工具封装了大量细节。读卷三时,你需要看到 Method、InvocationHandler、ClassLoader、VarHandle 这些原始形态。

同样,本书不把每个实验都做成完整框架。插件加载器只加载一个 ISBN 校验器,Agent 只计时一个方法,JMH 只测两个实现。例子小,才能把变量控制住;真实项目里的复杂度,应该由读者在自己的场景里叠加。

与站内其他内容的关系

  • 卷一《Java 编程入门》补齐语言基础。
  • 卷二《Java 编程实战》补齐工程化与服务端能力。
  • 卷四《Java 虚拟机原理与性能调优》补齐 JVM 内部与调优。
  • Spring Boot 三部曲补齐框架层;本书第 2 章的反射与代理、第 3 章的类加载,是理解 Spring 容器的基础。

如果时间有限,优先读第 1、4、6、9 章:它们分别对应类型、内存模型、虚拟线程和性能测量,是实际项目里最容易出错、也最容易被旧资料误导的部分。

开始之前

先把环境跑通:

export JAVA_HOME=~/.jdks/jdk-25.0.5+8/Contents/Home
export PATH="$JAVA_HOME/bin:$PATH"
java -version
javac -version
mvn -version

三条命令都显示 25.0.5 之后,再打开第 1 章。如果你用的是其他 25.x 补丁版本,绝大多数示例仍然成立;涉及具体输出的地方,以你本机为准。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「java」更多文章

  1. 《Netty 高性能网络编程》15.3 linkd-v2 验收与复盘
  2. 《Netty 高性能网络编程》15.2 限流、超时与降级
  3. 《Netty 高性能网络编程》15.1 网关路由与转发