Java 全栈知识体系:从 IDEA 高效开发到 JVM、并发与 Stream 实战

1. 写在前面:这份知识体系怎么用

Java 发展至今已近三十年,生态庞大、知识点密集。很多开发者的困境不是"没学过",而是"学过但串不起来":知道 synchronized 能加锁,却说不清锁升级过程;会用 Stream 写链式调用,却不知道它背后的惰性求值与并行陷阱;能背出 JVM 内存分区,却遇到 OOM 时无从下手。

本文试图解决这个问题。它按"工具 → 语言基础 → 面向对象 → 异常与 I/O → 集合与泛型 → 并发 → 反射与函数式"的顺序,把企业级开发真正会用到的 Java 知识串成一条线。每一部分都遵循同一个结构:是什么 → 为什么这样设计 → 怎么用 → 常见坑。

阅读建议:

  • 初学者:按顺序读,重点吃透第 3~6 章的语言基础与面向对象,这是后面一切的地基。
  • 有经验的开发者:可以跳过第 2 章,直接从第 7 章 JVM 开始,把并发(第 11 章)和集合(第 9 章)当作查漏补缺的重点。
  • 面试准备:第 7、9、11、13 章是高频考点集中区,建议配合每章末尾的"常见坑"一起看。

说明:本文示例基于 JDK 17(LTS)编写,绝大多数代码在 JDK 8 及以上均可运行。涉及新版本特性的地方会单独标注。

2. 开发环境与 IDEA 高效开发

2.1 JDK、JRE、JVM 的关系

三者是包含关系,理解它们能帮你避开很多环境问题:
#mermaid-svg-YHqoPrptSbv73v3I{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-YHqoPrptSbv73v3I .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-YHqoPrptSbv73v3I .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-YHqoPrptSbv73v3I .error-icon{fill:#552222;}#mermaid-svg-YHqoPrptSbv73v3I .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-YHqoPrptSbv73v3I .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-YHqoPrptSbv73v3I .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-YHqoPrptSbv73v3I .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-YHqoPrptSbv73v3I .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-YHqoPrptSbv73v3I .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-YHqoPrptSbv73v3I .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-YHqoPrptSbv73v3I .marker{fill:#333333;stroke:#333333;}#mermaid-svg-YHqoPrptSbv73v3I .marker.cross{stroke:#333333;}#mermaid-svg-YHqoPrptSbv73v3I svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-YHqoPrptSbv73v3I p{margin:0;}#mermaid-svg-YHqoPrptSbv73v3I .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-YHqoPrptSbv73v3I .cluster-label text{fill:#333;}#mermaid-svg-YHqoPrptSbv73v3I .cluster-label span{color:#333;}#mermaid-svg-YHqoPrptSbv73v3I .cluster-label span p{background-color:transparent;}#mermaid-svg-YHqoPrptSbv73v3I .label text,#mermaid-svg-YHqoPrptSbv73v3I span{fill:#333;color:#333;}#mermaid-svg-YHqoPrptSbv73v3I .node rect,#mermaid-svg-YHqoPrptSbv73v3I .node circle,#mermaid-svg-YHqoPrptSbv73v3I .node ellipse,#mermaid-svg-YHqoPrptSbv73v3I .node polygon,#mermaid-svg-YHqoPrptSbv73v3I .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-YHqoPrptSbv73v3I .rough-node .label text,#mermaid-svg-YHqoPrptSbv73v3I .node .label text,#mermaid-svg-YHqoPrptSbv73v3I .image-shape .label,#mermaid-svg-YHqoPrptSbv73v3I .icon-shape .label{text-anchor:middle;}#mermaid-svg-YHqoPrptSbv73v3I .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-YHqoPrptSbv73v3I .rough-node .label,#mermaid-svg-YHqoPrptSbv73v3I .node .label,#mermaid-svg-YHqoPrptSbv73v3I .image-shape .label,#mermaid-svg-YHqoPrptSbv73v3I .icon-shape .label{text-align:center;}#mermaid-svg-YHqoPrptSbv73v3I .node.clickable{cursor:pointer;}#mermaid-svg-YHqoPrptSbv73v3I .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-YHqoPrptSbv73v3I .arrowheadPath{fill:#333333;}#mermaid-svg-YHqoPrptSbv73v3I .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-YHqoPrptSbv73v3I .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-YHqoPrptSbv73v3I .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-YHqoPrptSbv73v3I .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-YHqoPrptSbv73v3I .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-YHqoPrptSbv73v3I .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-YHqoPrptSbv73v3I .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-YHqoPrptSbv73v3I .cluster text{fill:#333;}#mermaid-svg-YHqoPrptSbv73v3I .cluster span{color:#333;}#mermaid-svg-YHqoPrptSbv73v3I div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-YHqoPrptSbv73v3I .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-YHqoPrptSbv73v3I rect.text{fill:none;stroke-width:0;}#mermaid-svg-YHqoPrptSbv73v3I .icon-shape,#mermaid-svg-YHqoPrptSbv73v3I .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-YHqoPrptSbv73v3I .icon-shape p,#mermaid-svg-YHqoPrptSbv73v3I .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-YHqoPrptSbv73v3I .icon-shape .label rect,#mermaid-svg-YHqoPrptSbv73v3I .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-YHqoPrptSbv73v3I .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-YHqoPrptSbv73v3I .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-YHqoPrptSbv73v3I :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} JDK

Java Development Kit
JRE

Java Runtime Environment
开发工具

javac / javadoc / jdb / jshell
JVM

Java Virtual Machine
核心类库

java.lang / java.util / java.io ...

  • JVM:执行字节码的虚拟机,是"一次编写,到处运行"的根基。
  • JRE:JVM + 核心类库,只能运行 Java 程序,不能编译。
  • JDK:JRE + 开发工具,开发者必须安装 JDK。

从 JDK 11 开始,Oracle 不再单独提供 JRE 安装包,需要时可用 jlink 自行裁剪生成。

2.2 JDK 版本选择

版本 发布时间 是否 LTS 关键特性
JDK 8 2014 是 Lambda、Stream、新日期时间 API、Metaspace
JDK 11 2018 是 var 局部变量推断、HttpClient、ZGC 实验性
JDK 17 2021 是 sealed 类、record、switch 模式匹配预览、强封装 JDK 内部 API
JDK 21 2023 是 虚拟线程、record 模式、switch 模式匹配正式版

企业选型建议:新项目优先 JDK 17 或 21;存量系统停留在 JDK 8 的,升级前务必评估第三方依赖兼容性。

2.3 环境变量配置

Windows 下需要配置三个变量:

bash 复制代码
JAVA_HOME=C:\Program Files\Java\jdk-17
Path=%JAVA_HOME%\bin;%Path%
CLASSPATH=.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar

Linux/macOS 在 ~/.bashrc 或 ~/.zshrc 中:

bash 复制代码
export JAVA_HOME=/usr/lib/jvm/jdk-17
export PATH=$JAVA_HOME/bin:$PATH

验证:

bash 复制代码
java -version
javac -version

两个命令输出的版本号必须一致,否则说明 PATH 中存在多个 JDK,这是新手最常见的环境问题。

2.4 IDEA 核心配置

内存调优 :修改 Help → Edit Custom VM Options,默认堆内存往往偏小:

text 复制代码
-Xms1024m
-Xmx4096m
-XX:ReservedCodeCacheSize=512m

编码统一 :Settings → Editor → File Encodings 全部设为 UTF-8,避免中文乱码。

自动导包 :Settings → Editor → General → Auto Import,勾选 Add unambiguous imports on the fly 和 Optimize imports on the fly。

Maven 配置:指定本地仓库与阿里云镜像,加速依赖下载:

xml 复制代码
<mirror>
  <id>aliyunmaven</id>
  <mirrorOf>*</mirrorOf>
  <name>阿里云公共仓库</name>
  <url>https://maven.aliyun.com/repository/public</url>
</mirror>

2.5 必会快捷键

快捷键 作用
Ctrl + N 查找类
Ctrl + Shift + N 查找文件
Ctrl + Shift + Alt + N 查找符号(方法名/变量名)
Ctrl + Alt + L 格式化代码
Ctrl + Alt + O 优化导入
Shift + F6 重命名(安全重构)
Ctrl + Alt + V 提取变量
Ctrl + Alt + M 提取方法
Alt + Enter 万能提示(快速修复)
Ctrl + Shift + F 全局搜索
Ctrl + F9 编译项目
Shift + F9 Debug 运行
Alt + F8 表达式求值(Debug 时)
F8 / F7 单步跳过 / 单步进入

2.6 Debug 高级技巧

条件断点 :右键断点设置条件,例如 i == 100,避免循环里点几百次。

异常断点 :Ctrl + Shift + F8 打开断点面板,添加 NullPointerException,程序抛出该异常时自动停下,快速定位空指针源头。

字段断点:在字段声明处打断点,可监听该字段的读写,排查"值被谁改了"。

Drop Frame :Debug 时右键栈帧选择 Drop Frame,可回退到方法调用前重新执行,无需重启。

Evaluate Expression :Alt + F8 可执行任意表达式,包括调用方法、修改变量值,是排查线上问题复现的利器。

2.7 常用插件

  • Lombok:消除 getter/setter/构造器样板代码。
  • MyBatisX:Mapper 接口与 XML 之间跳转。
  • Alibaba Java Coding Guidelines:阿里规约静态检查。
  • SonarLint:本地代码质量扫描。
  • Rainbow Brackets:括号彩虹配色,嵌套深时救命。
  • GitToolBox:行内显示 Git 提交信息。
  • Key Promoter X:提示你正在用鼠标的操作对应的快捷键。

2.8 常见坑

  • 改了代码没生效 :检查是否开启了 Build project automatically,或 Maven 是否配置了热部署。
  • 中文乱码 :除了文件编码,还要检查 idea64.exe.vmoptions 中的 -Dfile.encoding=UTF-8。
  • 依赖冲突 :用 mvn dependency:tree 查看依赖树,Ctrl + Alt + Shift + U 可在 IDEA 中可视化冲突。

3. Java 基础语法

3.1 程序结构与入口

一个最简 Java 程序:

java 复制代码
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, Java!");
    }
}

逐项拆解:

  • public:访问修饰符,表示公开。
  • class:类声明关键字。
  • HelloWorld:类名,必须与文件名一致(public 类)。
  • static:静态方法,无需实例化即可调用。
  • void:无返回值。
  • main:JVM 约定的入口方法名。
  • String[] args:命令行参数数组。

3.2 标识符与命名规范

标识符规则:由字母、数字、下划线、美元符号组成,不能以数字开头,不能是关键字,区分大小写。

命名约定(企业开发必须遵守):

类型 规范 示例
类名 大驼峰 UserService
方法名 小驼峰 getUserName
变量名 小驼峰 orderCount
常量 全大写下划线 MAX_RETRY_COUNT
包名 全小写,域名倒置 com.example.order

3.3 关键字总览

Java 关键字共 50 个左右,按用途分类:

访问控制 :public、protected、private

类与接口 :class、interface、enum、extends、implements、abstract、final、static

流程控制 :if、else、switch、case、default、for、while、do、break、continue、return

异常 :try、catch、finally、throw、throws

包 :package、import

修饰符 :synchronized、volatile、transient、native、strictfp

类型 :byte、short、int、long、float、double、char、boolean、void

对象 :new、this、super、instanceof

字面量 :true、false、null

保留字 :goto、const(未使用,但不可作标识符)

3.4 数据类型

Java 是强类型语言,分为基本类型和引用类型。

八种基本类型:

类型 字节 范围 默认值 包装类
byte 1 -128 ~ 127 0 Byte
short 2 -32768 ~ 32767 0 Short
int 4 -2^31 ~ 2^31-1 0 Integer
long 8 -2^63 ~ 2^63-1 0L Long
float 4 ±3.4E-38 ~ ±3.4E38 0.0f Float
double 8 ±1.7E-308 ~ ±1.7E308 0.0d Double
char 2 0 ~ 65535 '\u0000' Character
boolean 1(JVM 实现相关) true/false false Boolean

自动类型转换(小 → 大):

java 复制代码
byte b = 10;
int i = b;        // 自动转换
long l = i;       // 自动转换
double d = l;     // 自动转换

强制类型转换(大 → 小,可能丢失精度):

java 复制代码
double d = 3.99;
int i = (int) d;  // 结果为 3,小数部分截断

精度陷阱:

java 复制代码
System.out.println(0.1 + 0.2);        // 0.30000000000000004
System.out.println(0.1f + 0.2f);      // 0.3

浮点数用二进制表示,0.1 无法精确表示。涉及金额计算必须用 BigDecimal:

java 复制代码
BigDecimal a = new BigDecimal("0.1");
BigDecimal b = new BigDecimal("0.2");
System.out.println(a.add(b));         // 0.3

注意 new BigDecimal(0.1) 会得到 0.1000000000000000055511151231257827...,必须用字符串构造器。

3.5 运算符

算术运算符 :+ - * / % ++ --

java 复制代码
int a = 10, b = 3;
System.out.println(a / b);   // 3,整数除法截断
System.out.println(a % b);   // 1
System.out.println(a / 3.0); // 3.3333333333333335

自增自减:

java 复制代码
int i = 5;
int j = i++;  // j = 5, i = 6(先赋值后自增)
int k = ++i;  // k = 7, i = 7(先自增后赋值)

赋值运算符 := += -= *= /= %=

java 复制代码
short s = 1;
s += 1;       // 编译通过,隐含强制转换
// s = s + 1; // 编译错误,int 不能赋给 short

比较运算符 :== != > < >= <=,返回 boolean。

逻辑运算符 :&& || !

java 复制代码
// && 短路:左边为 false 时不计算右边
if (obj != null && obj.getName() != null) { ... }

// & 不短路:两边都会计算

位运算符 :& | ^ ~ << >> >>>

java 复制代码
int x = 5;    // 0101
int y = 3;    // 0011
System.out.println(x & y);   // 0001 = 1
System.out.println(x | y);   // 0111 = 7
System.out.println(x ^ y);   // 0110 = 6
System.out.println(x << 1);  // 1010 = 10
System.out.println(x >> 1);  // 0010 = 2
System.out.println(-8 >>> 1); // 无符号右移,高位补 0

三元运算符 :条件 ? 值1 : 值2

java 复制代码
int max = a > b ? a : b;

运算符优先级(从高到低,记不住就加括号):

text 复制代码
() [] .  →  ! ~ ++ --  →  * / %  →  + -  →  << >> >>>  →  < <= > >= instanceof
→  == !=  →  &  →  ^  →  |  →  &&  →  ||  →  ?:  →  = += -= ...

3.6 流程控制

if-else:

java 复制代码
int score = 85;
if (score >= 90) {
    System.out.println("优秀");
} else if (score >= 60) {
    System.out.println("及格");
} else {
    System.out.println("不及格");
}

switch:

java 复制代码
String day = "MON";
switch (day) {
    case "MON":
    case "TUE":
        System.out.println("工作日");
        break;
    case "SAT":
    case "SUN":
        System.out.println("周末");
        break;
    default:
        System.out.println("未知");
}

JDK 14+ 的箭头语法(无穿透,更安全):

java 复制代码
switch (day) {
    case "MON", "TUE" -> System.out.println("工作日");
    case "SAT", "SUN" -> System.out.println("周末");
    default -> System.out.println("未知");
}

for 循环:

java 复制代码
for (int i = 0; i < 10; i++) {
    if (i == 5) continue;   // 跳过本次
    if (i == 8) break;      // 结束循环
    System.out.println(i);
}

增强 for(遍历数组/集合):

java 复制代码
int[] arr = {1, 2, 3};
for (int n : arr) {
    System.out.println(n);
}

while / do-while:

java 复制代码
int i = 0;
while (i < 5) { i++; }

int j = 0;
do {
    j++;
} while (j < 5);   // 至少执行一次

3.7 数组

java 复制代码
// 声明与初始化
int[] arr1 = new int[5];              // 默认 0
int[] arr2 = {1, 2, 3, 4, 5};
int[] arr3 = new int[]{1, 2, 3};

// 二维数组
int[][] matrix = new int[3][4];
int[][] matrix2 = {{1, 2}, {3, 4}};

// 遍历
for (int i = 0; i < arr2.length; i++) {
    System.out.println(arr2[i]);
}

数组长度固定,扩容需借助 Arrays.copyOf:

java 复制代码
int[] old = {1, 2, 3};
int[] bigger = Arrays.copyOf(old, 10);   // 长度 10,后 7 位为 0

3.8 常见坑

  • 整数除法 :1 / 2 结果是 0,要小数必须写 1.0 / 2。
  • 浮点比较 :不要用 == 比较 double,用 Math.abs(a - b) < 1e-6。
  • 数组越界 :ArrayIndexOutOfBoundsException,循环条件务必用 length。
  • switch 穿透 :忘记 break 会继续执行下一个 case,箭头语法可避免。

4. 面向对象三大特性

4.1 类与对象

类是模板,对象是实例。

java 复制代码
public class Person {
    // 成员变量
    private String name;
    private int age;

    // 构造器
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // 方法
    public void introduce() {
        System.out.println("我是 " + name + ",今年 " + age + " 岁");
    }

    // getter/setter
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
}

// 使用
Person p = new Person("张三", 25);
p.introduce();

对象创建过程:

  1. 检查类是否已加载,未加载则先加载。
  2. 在堆中分配内存。
  3. 成员变量赋默认值(0/null/false)。
  4. 执行构造器,赋初始值。
  5. 返回对象引用。

4.2 封装

封装的核心是隐藏实现细节,暴露安全接口。

java 复制代码
public class Account {
    private BigDecimal balance;   // 私有,外部不可直接改

    public Account(BigDecimal initial) {
        if (initial.compareTo(BigDecimal.ZERO) < 0) {
            throw new IllegalArgumentException("初始余额不能为负");
        }
        this.balance = initial;
    }

    public BigDecimal getBalance() {
        return balance;
    }

    public void deposit(BigDecimal amount) {
        if (amount.compareTo(BigDecimal.ZERO) <= 0) {
            throw new IllegalArgumentException("存款金额必须大于 0");
        }
        this.balance = this.balance.add(amount);
    }

    public void withdraw(BigDecimal amount) {
        if (amount.compareTo(balance) > 0) {
            throw new IllegalStateException("余额不足");
        }
        this.balance = this.balance.subtract(amount);
    }
}

封装的价值:外部无法绕过校验直接改 balance,所有变更都经过受控方法,业务规则不会被破坏。

访问修饰符对比:

修饰符 本类 同包 子类 其他
private ✓ ✗ ✗ ✗
默认(无) ✓ ✓ ✗ ✗
protected ✓ ✓ ✓ ✗
public ✓ ✓ ✓ ✓

4.3 继承

继承表达"is-a"关系,子类复用父类能力并可扩展。

java 复制代码
public class Animal {
    protected String name;

    public Animal(String name) {
        this.name = name;
    }

    public void eat() {
        System.out.println(name + " 在吃东西");
    }

    public void sleep() {
        System.out.println(name + " 在睡觉");
    }
}

public class Dog extends Animal {
    public Dog(String name) {
        super(name);   // 必须调用父类构造器
    }

    @Override
    public void eat() {
        System.out.println(name + " 在啃骨头");
    }

    public void bark() {
        System.out.println(name + " 汪汪叫");
    }
}

关键规则:

  • Java 单继承,一个类只能有一个直接父类。
  • 子类构造器必须先调用父类构造器(super()),不写则默认调用无参构造。
  • 父类无无参构造时,子类必须显式 super(参数)。
  • @Override 注解不是必须的,但强烈建议加,编译期可校验签名。

方法重写(Override)规则:

  • 方法名、参数列表必须相同。
  • 返回类型可以是父类返回类型的子类(协变返回)。
  • 访问权限不能比父类更严格。
  • 不能重写 final、static、private 方法。

方法重载(Overload):同一类中方法名相同、参数列表不同,与返回值无关。

java 复制代码
public void print(int x) { }
public void print(String x) { }
public void print(int x, int y) { }

4.4 多态

多态是面向对象最核心的能力:父类引用指向子类对象,调用时执行子类实现。

java 复制代码
Animal a1 = new Dog("旺财");
Animal a2 = new Cat("咪咪");

a1.eat();   // 旺财 在啃骨头
a2.eat();   // 咪咪 在吃鱼

多态的三个必要条件:

  1. 有继承或实现关系。
  2. 子类重写父类方法。
  3. 父类引用指向子类对象。

动态绑定原理:JVM 通过对象的实际类型在运行时查找方法,而非编译时的声明类型。这就是"编译看左边,运行看右边"。

向上转型与向下转型:

java 复制代码
Animal a = new Dog("旺财");   // 向上转型,自动
Dog d = (Dog) a;              // 向下转型,需强转
Cat c = (Cat) a;              // 运行时抛 ClassCastException

// 安全转型
if (a instanceof Dog) {
    Dog dog = (Dog) a;
}

// JDK 16+ 模式匹配
if (a instanceof Dog dog) {
    dog.bark();
}

多态的实际价值:面向接口编程,新增实现无需修改调用方代码。

java 复制代码
public interface Payment {
    void pay(BigDecimal amount);
}

public class AlipayPayment implements Payment {
    public void pay(BigDecimal amount) {
        System.out.println("支付宝支付 " + amount);
    }
}

public class WechatPayment implements Payment {
    public void pay(BigDecimal amount) {
        System.out.println("微信支付 " + amount);
    }
}

// 调用方只依赖接口
public void checkout(Payment payment, BigDecimal amount) {
    payment.pay(amount);
}

4.5 抽象类与接口

抽象类 :用 abstract 修饰,不能实例化,可包含抽象方法和具体方法。

java 复制代码
public abstract class Shape {
    protected String color;

    public Shape(String color) {
        this.color = color;
    }

    // 抽象方法,子类必须实现
    public abstract double area();

    // 具体方法,子类可复用
    public void describe() {
        System.out.println(color + " 图形,面积 " + area());
    }
}

public class Circle extends Shape {
    private double radius;

    public Circle(String color, double radius) {
        super(color);
        this.radius = radius;
    }

    @Override
    public double area() {
        return Math.PI * radius * radius;
    }
}

接口 :用 interface 声明,JDK 8 后支持默认方法和静态方法。

java 复制代码
public interface Flyable {
    // 常量,隐式 public static final
    int MAX_HEIGHT = 10000;

    // 抽象方法,隐式 public abstract
    void fly();

    // 默认方法,JDK 8+
    default void land() {
        System.out.println("降落中...");
    }

    // 静态方法,JDK 8+
    static Flyable create() {
        return () -> System.out.println("飞行中...");
    }
}

抽象类 vs 接口:

维度 抽象类 接口
继承数量 单继承 多实现
成员变量 任意 只能是常量
方法 抽象 + 具体 抽象 + 默认 + 静态(JDK 8+)
构造器 有 无
语义 is-a can-do
适用 有共享代码的模板 定义能力契约

选择原则:优先接口,需要共享状态或模板方法时用抽象类。

4.6 内部类

成员内部类:依赖外部类实例。

java 复制代码
public class Outer {
    private int x = 10;

    class Inner {
        void print() {
            System.out.println(x);   // 可直接访问外部私有成员
        }
    }
}

Outer.Inner inner = new Outer().new Inner();

静态内部类:不依赖外部实例,最常用。

java 复制代码
public class Outer {
    static class StaticInner {
        void print() { System.out.println("静态内部类"); }
    }
}

Outer.StaticInner inner = new Outer.StaticInner();

局部内部类:定义在方法内。

匿名内部类:一次性实现,Lambda 的前身。

java 复制代码
Runnable r = new Runnable() {
    @Override
    public void run() {
        System.out.println("匿名内部类");
    }
};

4.7 常见坑

  • 重写 vs 重载混淆:重写是父子类之间,重载是同名不同参。
  • 构造器中调用可重写方法:父类构造器执行时子类字段尚未初始化,可能拿到 null。
  • equals 与 hashCode 必须同时重写 :只重写 equals 会导致 HashMap 行为异常。
  • 接口默认方法冲突:多个接口有同名默认方法时,实现类必须显式重写。

5. 异常处理

5.1 异常体系

#mermaid-svg-43yflKPyYvKGa7mB{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-43yflKPyYvKGa7mB .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-43yflKPyYvKGa7mB .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-43yflKPyYvKGa7mB .error-icon{fill:#552222;}#mermaid-svg-43yflKPyYvKGa7mB .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-43yflKPyYvKGa7mB .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-43yflKPyYvKGa7mB .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-43yflKPyYvKGa7mB .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-43yflKPyYvKGa7mB .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-43yflKPyYvKGa7mB .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-43yflKPyYvKGa7mB .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-43yflKPyYvKGa7mB .marker{fill:#333333;stroke:#333333;}#mermaid-svg-43yflKPyYvKGa7mB .marker.cross{stroke:#333333;}#mermaid-svg-43yflKPyYvKGa7mB svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-43yflKPyYvKGa7mB p{margin:0;}#mermaid-svg-43yflKPyYvKGa7mB .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-43yflKPyYvKGa7mB .cluster-label text{fill:#333;}#mermaid-svg-43yflKPyYvKGa7mB .cluster-label span{color:#333;}#mermaid-svg-43yflKPyYvKGa7mB .cluster-label span p{background-color:transparent;}#mermaid-svg-43yflKPyYvKGa7mB .label text,#mermaid-svg-43yflKPyYvKGa7mB span{fill:#333;color:#333;}#mermaid-svg-43yflKPyYvKGa7mB .node rect,#mermaid-svg-43yflKPyYvKGa7mB .node circle,#mermaid-svg-43yflKPyYvKGa7mB .node ellipse,#mermaid-svg-43yflKPyYvKGa7mB .node polygon,#mermaid-svg-43yflKPyYvKGa7mB .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-43yflKPyYvKGa7mB .rough-node .label text,#mermaid-svg-43yflKPyYvKGa7mB .node .label text,#mermaid-svg-43yflKPyYvKGa7mB .image-shape .label,#mermaid-svg-43yflKPyYvKGa7mB .icon-shape .label{text-anchor:middle;}#mermaid-svg-43yflKPyYvKGa7mB .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-43yflKPyYvKGa7mB .rough-node .label,#mermaid-svg-43yflKPyYvKGa7mB .node .label,#mermaid-svg-43yflKPyYvKGa7mB .image-shape .label,#mermaid-svg-43yflKPyYvKGa7mB .icon-shape .label{text-align:center;}#mermaid-svg-43yflKPyYvKGa7mB .node.clickable{cursor:pointer;}#mermaid-svg-43yflKPyYvKGa7mB .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-43yflKPyYvKGa7mB .arrowheadPath{fill:#333333;}#mermaid-svg-43yflKPyYvKGa7mB .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-43yflKPyYvKGa7mB .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-43yflKPyYvKGa7mB .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-43yflKPyYvKGa7mB .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-43yflKPyYvKGa7mB .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-43yflKPyYvKGa7mB .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-43yflKPyYvKGa7mB .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-43yflKPyYvKGa7mB .cluster text{fill:#333;}#mermaid-svg-43yflKPyYvKGa7mB .cluster span{color:#333;}#mermaid-svg-43yflKPyYvKGa7mB div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-43yflKPyYvKGa7mB .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-43yflKPyYvKGa7mB rect.text{fill:none;stroke-width:0;}#mermaid-svg-43yflKPyYvKGa7mB .icon-shape,#mermaid-svg-43yflKPyYvKGa7mB .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-43yflKPyYvKGa7mB .icon-shape p,#mermaid-svg-43yflKPyYvKGa7mB .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-43yflKPyYvKGa7mB .icon-shape .label rect,#mermaid-svg-43yflKPyYvKGa7mB .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-43yflKPyYvKGa7mB .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-43yflKPyYvKGa7mB .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-43yflKPyYvKGa7mB :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Throwable
Error

严重错误,程序无法处理
Exception
RuntimeException

非受检异常
受检异常

Checked Exception
OutOfMemoryError
StackOverflowError
NullPointerException
IllegalArgumentException
IndexOutOfBoundsException
IOException
SQLException
ClassNotFoundException

  • Error:JVM 层面的严重问题,如内存溢出、栈溢出,程序不应尝试捕获。
  • 受检异常 :编译器强制要求处理(try-catch 或 throws),如 IOException。
  • 非受检异常 :RuntimeException 及其子类,编译器不强制处理,通常是编程错误。

5.2 try-catch-finally

java 复制代码
public String readFile(String path) {
    BufferedReader reader = null;
    try {
        reader = new BufferedReader(new FileReader(path));
        return reader.readLine();
    } catch (FileNotFoundException e) {
        System.err.println("文件不存在:" + path);
        return null;
    } catch (IOException e) {
        System.err.println("读取失败:" + e.getMessage());
        return null;
    } finally {
        if (reader != null) {
            try {
                reader.close();
            } catch (IOException ignored) {
            }
        }
    }
}

finally 执行时机:

  • 无论是否抛异常都会执行。
  • try 或 catch 中有 return 时,finally 仍会执行,且在 return 表达式求值之后、方法返回之前。
  • finally 中也有 return 会覆盖前面的返回值(强烈不推荐)。
java 复制代码
public int test() {
    try {
        return 1;
    } finally {
        return 2;   // 最终返回 2
    }
}

5.3 try-with-resources

JDK 7+ 引入,自动关闭实现了 AutoCloseable 的资源,代码更简洁且不会漏关。

java 复制代码
public String readFile(String path) throws IOException {
    try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
        return reader.readLine();
    }
}

多个资源用分号分隔,关闭顺序与声明顺序相反:

java 复制代码
try (InputStream in = new FileInputStream("in.txt");
     OutputStream out = new FileOutputStream("out.txt")) {
    // 使用
}

5.4 throw 与 throws

  • throw:在方法体内主动抛出异常对象。
  • throws:在方法签名上声明可能抛出的异常,交给调用方处理。
java 复制代码
public void validateAge(int age) throws IllegalArgumentException {
    if (age < 0 || age > 150) {
        throw new IllegalArgumentException("年龄不合法:" + age);
    }
}

5.5 自定义异常

java 复制代码
public class BusinessException extends RuntimeException {
    private final String code;

    public BusinessException(String code, String message) {
        super(message);
        this.code = code;
    }

    public BusinessException(String code, String message, Throwable cause) {
        super(message, cause);
        this.code = code;
    }

    public String getCode() {
        return code;
    }
}

企业开发中,业务异常通常继承 RuntimeException,避免污染方法签名;系统异常才用受检异常。

5.6 异常处理最佳实践

  1. 不要吞异常 :catch (Exception e) {} 是反模式,至少记录日志。
  2. 不要用异常控制流程:异常开销大,正常分支用 if 判断。
  3. 捕获具体异常:先捕获子类,再捕获父类,否则编译报错。
  4. 保留原始异常 :包装时传 cause,否则堆栈信息丢失。
  5. finally 中不要 return:会覆盖 try 中的返回值,且吞掉异常。
  6. 日志要带上下文:记录关键参数,便于排查。
java 复制代码
// 反例
try {
    process();
} catch (Exception e) {
    // 什么都没做
}

// 正例
try {
    process(orderId);
} catch (BusinessException e) {
    log.warn("业务处理失败,orderId={}, code={}", orderId, e.getCode(), e);
    throw e;
} catch (Exception e) {
    log.error("系统异常,orderId={}", orderId, e);
    throw new BusinessException("SYS_ERROR", "系统繁忙", e);
}

5.7 常见坑

  • finally 中关闭资源抛异常:会覆盖 try 中的异常,用 try-with-resources 可避免。
  • e.printStackTrace() 上生产:输出到标准错误流,日志系统采集不到,必须用日志框架。
  • 捕获后不处理:静默失败是最难排查的 bug。
  • 异常信息拼接字符串 :log.error("失败:" + e) 会丢失堆栈,应写成 log.error("失败", e)。

6. I/O 流

6.1 流的分类

#mermaid-svg-n57bErUhBo9taplA{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-n57bErUhBo9taplA .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-n57bErUhBo9taplA .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-n57bErUhBo9taplA .error-icon{fill:#552222;}#mermaid-svg-n57bErUhBo9taplA .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-n57bErUhBo9taplA .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-n57bErUhBo9taplA .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-n57bErUhBo9taplA .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-n57bErUhBo9taplA .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-n57bErUhBo9taplA .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-n57bErUhBo9taplA .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-n57bErUhBo9taplA .marker{fill:#333333;stroke:#333333;}#mermaid-svg-n57bErUhBo9taplA .marker.cross{stroke:#333333;}#mermaid-svg-n57bErUhBo9taplA svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-n57bErUhBo9taplA p{margin:0;}#mermaid-svg-n57bErUhBo9taplA .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-n57bErUhBo9taplA .cluster-label text{fill:#333;}#mermaid-svg-n57bErUhBo9taplA .cluster-label span{color:#333;}#mermaid-svg-n57bErUhBo9taplA .cluster-label span p{background-color:transparent;}#mermaid-svg-n57bErUhBo9taplA .label text,#mermaid-svg-n57bErUhBo9taplA span{fill:#333;color:#333;}#mermaid-svg-n57bErUhBo9taplA .node rect,#mermaid-svg-n57bErUhBo9taplA .node circle,#mermaid-svg-n57bErUhBo9taplA .node ellipse,#mermaid-svg-n57bErUhBo9taplA .node polygon,#mermaid-svg-n57bErUhBo9taplA .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-n57bErUhBo9taplA .rough-node .label text,#mermaid-svg-n57bErUhBo9taplA .node .label text,#mermaid-svg-n57bErUhBo9taplA .image-shape .label,#mermaid-svg-n57bErUhBo9taplA .icon-shape .label{text-anchor:middle;}#mermaid-svg-n57bErUhBo9taplA .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-n57bErUhBo9taplA .rough-node .label,#mermaid-svg-n57bErUhBo9taplA .node .label,#mermaid-svg-n57bErUhBo9taplA .image-shape .label,#mermaid-svg-n57bErUhBo9taplA .icon-shape .label{text-align:center;}#mermaid-svg-n57bErUhBo9taplA .node.clickable{cursor:pointer;}#mermaid-svg-n57bErUhBo9taplA .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-n57bErUhBo9taplA .arrowheadPath{fill:#333333;}#mermaid-svg-n57bErUhBo9taplA .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-n57bErUhBo9taplA .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-n57bErUhBo9taplA .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-n57bErUhBo9taplA .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-n57bErUhBo9taplA .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-n57bErUhBo9taplA .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-n57bErUhBo9taplA .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-n57bErUhBo9taplA .cluster text{fill:#333;}#mermaid-svg-n57bErUhBo9taplA .cluster span{color:#333;}#mermaid-svg-n57bErUhBo9taplA div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-n57bErUhBo9taplA .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-n57bErUhBo9taplA rect.text{fill:none;stroke-width:0;}#mermaid-svg-n57bErUhBo9taplA .icon-shape,#mermaid-svg-n57bErUhBo9taplA .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-n57bErUhBo9taplA .icon-shape p,#mermaid-svg-n57bErUhBo9taplA .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-n57bErUhBo9taplA .icon-shape .label rect,#mermaid-svg-n57bErUhBo9taplA .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-n57bErUhBo9taplA .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-n57bErUhBo9taplA .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-n57bErUhBo9taplA :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Java I/O
字节流

InputStream / OutputStream
字符流

Reader / Writer
文件流

FileInputStream
缓冲流

BufferedInputStream
对象流

ObjectInputStream
文件流

FileReader
缓冲流

BufferedReader
转换流

InputStreamReader

  • 按方向:输入流(读)、输出流(写)。
  • 按单位:字节流(8 位)、字符流(16 位,处理文本)。
  • 按角色:节点流(直接连数据源)、处理流(包装节点流增强功能)。

6.2 字节流

java 复制代码
// 文件复制
public void copyFile(String src, String dest) throws IOException {
    try (FileInputStream in = new FileInputStream(src);
         FileOutputStream out = new FileOutputStream(dest)) {
        byte[] buffer = new byte[8192];
        int len;
        while ((len = in.read(buffer)) != -1) {
            out.write(buffer, 0, len);
        }
    }
}

必须用缓冲区:逐字节读写会频繁触发系统调用,性能极差。8KB 缓冲区是常见选择。

6.3 字符流

java 复制代码
public void writeText(String path, String content) throws IOException {
    try (BufferedWriter writer = new BufferedWriter(new FileWriter(path))) {
        writer.write(content);
        writer.newLine();
    }
}

public List<String> readLines(String path) throws IOException {
    List<String> lines = new ArrayList<>();
    try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
        String line;
        while ((line = reader.readLine()) != null) {
            lines.add(line);
        }
    }
    return lines;
}

6.4 转换流

字节流与字符流的桥梁,可指定编码:

java 复制代码
try (BufferedReader reader = new BufferedReader(
        new InputStreamReader(new FileInputStream("data.txt"), StandardCharsets.UTF_8))) {
    String line;
    while ((line = reader.readLine()) != null) {
        System.out.println(line);
    }
}

编码问题 :FileReader 使用平台默认编码,跨平台易乱码,生产代码应显式指定 StandardCharsets.UTF_8。

6.5 对象序列化

java 复制代码
public class User implements Serializable {
    private static final long serialVersionUID = 1L;

    private String name;
    private transient String password;   // transient 不参与序列化

    // getter/setter 省略
}

// 序列化
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user.dat"))) {
    oos.writeObject(new User("张三", "123456"));
}

// 反序列化
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.dat"))) {
    User user = (User) ois.readObject();
}

serialVersionUID 的作用 :类结构变化时,若 UID 不一致会抛 InvalidClassException。显式声明可保证兼容性。

安全提示:反序列化不可信数据存在远程代码执行风险,生产环境应避免或使用白名单过滤。

6.6 NIO 简介

JDK 1.4 引入 NIO,核心是 Channel、Buffer、Selector,适合高并发网络编程。

java 复制代码
// NIO 读文件
try (FileChannel channel = FileChannel.open(Paths.get("data.txt"), StandardOpenOption.READ)) {
    ByteBuffer buffer = ByteBuffer.allocate(1024);
    while (channel.read(buffer) != -1) {
        buffer.flip();
        while (buffer.hasRemaining()) {
            System.out.print((char) buffer.get());
        }
        buffer.clear();
    }
}

NIO vs 传统 I/O:

维度 BIO NIO
模型 阻塞 非阻塞(多路复用)
单位 流 通道 + 缓冲区
适用 连接少、数据量大 连接多、数据量小
代表 Socket Netty

JDK 7 的 NIO.2 引入 Files、Paths,日常文件操作更简洁:

java 复制代码
List<String> lines = Files.readAllLines(Paths.get("data.txt"), StandardCharsets.UTF_8);
Files.write(Paths.get("out.txt"), lines, StandardCharsets.UTF_8);
Files.copy(Paths.get("a.txt"), Paths.get("b.txt"), StandardCopyOption.REPLACE_EXISTING);

6.7 常见坑

  • 忘记关闭流 :文件句柄泄漏,最终导致 Too many open files,用 try-with-resources。
  • 缓冲区太小:1 字节缓冲区等于没缓冲。
  • 编码不一致:读写两端编码不同必然乱码。
  • read() 返回 -1 判断错误 :-1 表示流结束,不是读到 -1 这个字节。

7. JVM 内存模型与 GC 机制

7.1 运行时数据区

#mermaid-svg-QvoHXfl6DLxfGrzz{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-QvoHXfl6DLxfGrzz .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-QvoHXfl6DLxfGrzz .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-QvoHXfl6DLxfGrzz .error-icon{fill:#552222;}#mermaid-svg-QvoHXfl6DLxfGrzz .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-QvoHXfl6DLxfGrzz .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-QvoHXfl6DLxfGrzz .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-QvoHXfl6DLxfGrzz .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-QvoHXfl6DLxfGrzz .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-QvoHXfl6DLxfGrzz .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-QvoHXfl6DLxfGrzz .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-QvoHXfl6DLxfGrzz .marker{fill:#333333;stroke:#333333;}#mermaid-svg-QvoHXfl6DLxfGrzz .marker.cross{stroke:#333333;}#mermaid-svg-QvoHXfl6DLxfGrzz svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-QvoHXfl6DLxfGrzz p{margin:0;}#mermaid-svg-QvoHXfl6DLxfGrzz .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-QvoHXfl6DLxfGrzz .cluster-label text{fill:#333;}#mermaid-svg-QvoHXfl6DLxfGrzz .cluster-label span{color:#333;}#mermaid-svg-QvoHXfl6DLxfGrzz .cluster-label span p{background-color:transparent;}#mermaid-svg-QvoHXfl6DLxfGrzz .label text,#mermaid-svg-QvoHXfl6DLxfGrzz span{fill:#333;color:#333;}#mermaid-svg-QvoHXfl6DLxfGrzz .node rect,#mermaid-svg-QvoHXfl6DLxfGrzz .node circle,#mermaid-svg-QvoHXfl6DLxfGrzz .node ellipse,#mermaid-svg-QvoHXfl6DLxfGrzz .node polygon,#mermaid-svg-QvoHXfl6DLxfGrzz .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-QvoHXfl6DLxfGrzz .rough-node .label text,#mermaid-svg-QvoHXfl6DLxfGrzz .node .label text,#mermaid-svg-QvoHXfl6DLxfGrzz .image-shape .label,#mermaid-svg-QvoHXfl6DLxfGrzz .icon-shape .label{text-anchor:middle;}#mermaid-svg-QvoHXfl6DLxfGrzz .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-QvoHXfl6DLxfGrzz .rough-node .label,#mermaid-svg-QvoHXfl6DLxfGrzz .node .label,#mermaid-svg-QvoHXfl6DLxfGrzz .image-shape .label,#mermaid-svg-QvoHXfl6DLxfGrzz .icon-shape .label{text-align:center;}#mermaid-svg-QvoHXfl6DLxfGrzz .node.clickable{cursor:pointer;}#mermaid-svg-QvoHXfl6DLxfGrzz .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-QvoHXfl6DLxfGrzz .arrowheadPath{fill:#333333;}#mermaid-svg-QvoHXfl6DLxfGrzz .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-QvoHXfl6DLxfGrzz .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-QvoHXfl6DLxfGrzz .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-QvoHXfl6DLxfGrzz .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-QvoHXfl6DLxfGrzz .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-QvoHXfl6DLxfGrzz .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-QvoHXfl6DLxfGrzz .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-QvoHXfl6DLxfGrzz .cluster text{fill:#333;}#mermaid-svg-QvoHXfl6DLxfGrzz .cluster span{color:#333;}#mermaid-svg-QvoHXfl6DLxfGrzz div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-QvoHXfl6DLxfGrzz .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-QvoHXfl6DLxfGrzz rect.text{fill:none;stroke-width:0;}#mermaid-svg-QvoHXfl6DLxfGrzz .icon-shape,#mermaid-svg-QvoHXfl6DLxfGrzz .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-QvoHXfl6DLxfGrzz .icon-shape p,#mermaid-svg-QvoHXfl6DLxfGrzz .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-QvoHXfl6DLxfGrzz .icon-shape .label rect,#mermaid-svg-QvoHXfl6DLxfGrzz .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-QvoHXfl6DLxfGrzz .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-QvoHXfl6DLxfGrzz .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-QvoHXfl6DLxfGrzz :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} JVM 运行时数据区
线程私有
线程共享
程序计数器 PC
虚拟机栈
本地方法栈
堆 Heap
方法区 / 元空间
运行时常量池

程序计数器:记录当前线程执行的字节码行号,唯一不会 OOM 的区域。

虚拟机栈 :每个方法调用创建一个栈帧,存放局部变量表、操作数栈、动态链接、返回地址。栈深度超限抛 StackOverflowError。

本地方法栈:为 native 方法服务。

堆:对象实例的主要分配区域,GC 的主战场。分新生代(Eden + Survivor)和老年代。

方法区 :存储类元信息、常量、静态变量。JDK 8 后用元空间(Metaspace)实现,使用本地内存,不再受 -XX:MaxPermSize 限制。

7.2 对象创建与内存分配

java 复制代码
User user = new User();

执行过程:

  1. 检查常量池是否有 User 的符号引用,未加载则触发类加载。
  2. 在堆中分配内存(指针碰撞或空闲列表)。
  3. 内存空间初始化为零值。
  4. 设置对象头(Mark Word、类型指针)。
  5. 执行构造器。

对象内存布局:

  • 对象头:Mark Word(哈希码、GC 分代年龄、锁状态标志)+ 类型指针。
  • 实例数据:字段值。
  • 对齐填充:保证 8 字节对齐。

逃逸分析:JIT 编译器分析对象是否逃逸出方法,未逃逸可栈上分配或标量替换,减少堆压力。

7.3 垃圾判定算法

引用计数法:每个对象维护引用计数,为 0 即可回收。缺点是无法处理循环引用,Java 未采用。

可达性分析:从 GC Roots 出发遍历引用链,不可达的对象判定为垃圾。

GC Roots 包括:

  • 虚拟机栈中局部变量引用的对象。
  • 方法区中静态变量引用的对象。
  • 方法区中常量引用的对象。
  • 本地方法栈中 JNI 引用的对象。
  • 活跃线程。

四种引用:

引用类型 回收时机 典型用途
强引用 永不回收(除非不可达) 普通对象
软引用 SoftReference 内存不足时回收 缓存
弱引用 WeakReference 下次 GC 必回收 ThreadLocalMap 的 key
虚引用 PhantomReference 随时可能回收,仅用于跟踪 堆外内存管理

7.4 垃圾回收算法

标记-清除:标记存活对象,清除未标记的。缺点:产生内存碎片。

复制算法:将内存分两块,存活对象复制到另一块,清空当前块。优点:无碎片、效率高;缺点:浪费一半空间。新生代采用此算法(Eden : Survivor = 8 : 1 : 1)。

标记-整理:标记后将存活对象向一端移动,清理边界外内存。无碎片,但移动对象开销大。老年代常用。

分代收集:根据对象存活周期分区,新生代用复制,老年代用标记-整理/标记-清除。

7.5 分代回收流程

#mermaid-svg-XKrApJd30kq15OBB{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-XKrApJd30kq15OBB .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-XKrApJd30kq15OBB .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-XKrApJd30kq15OBB .error-icon{fill:#552222;}#mermaid-svg-XKrApJd30kq15OBB .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-XKrApJd30kq15OBB .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-XKrApJd30kq15OBB .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-XKrApJd30kq15OBB .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-XKrApJd30kq15OBB .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-XKrApJd30kq15OBB .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-XKrApJd30kq15OBB .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-XKrApJd30kq15OBB .marker{fill:#333333;stroke:#333333;}#mermaid-svg-XKrApJd30kq15OBB .marker.cross{stroke:#333333;}#mermaid-svg-XKrApJd30kq15OBB svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-XKrApJd30kq15OBB p{margin:0;}#mermaid-svg-XKrApJd30kq15OBB .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-XKrApJd30kq15OBB .cluster-label text{fill:#333;}#mermaid-svg-XKrApJd30kq15OBB .cluster-label span{color:#333;}#mermaid-svg-XKrApJd30kq15OBB .cluster-label span p{background-color:transparent;}#mermaid-svg-XKrApJd30kq15OBB .label text,#mermaid-svg-XKrApJd30kq15OBB span{fill:#333;color:#333;}#mermaid-svg-XKrApJd30kq15OBB .node rect,#mermaid-svg-XKrApJd30kq15OBB .node circle,#mermaid-svg-XKrApJd30kq15OBB .node ellipse,#mermaid-svg-XKrApJd30kq15OBB .node polygon,#mermaid-svg-XKrApJd30kq15OBB .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-XKrApJd30kq15OBB .rough-node .label text,#mermaid-svg-XKrApJd30kq15OBB .node .label text,#mermaid-svg-XKrApJd30kq15OBB .image-shape .label,#mermaid-svg-XKrApJd30kq15OBB .icon-shape .label{text-anchor:middle;}#mermaid-svg-XKrApJd30kq15OBB .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-XKrApJd30kq15OBB .rough-node .label,#mermaid-svg-XKrApJd30kq15OBB .node .label,#mermaid-svg-XKrApJd30kq15OBB .image-shape .label,#mermaid-svg-XKrApJd30kq15OBB .icon-shape .label{text-align:center;}#mermaid-svg-XKrApJd30kq15OBB .node.clickable{cursor:pointer;}#mermaid-svg-XKrApJd30kq15OBB .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-XKrApJd30kq15OBB .arrowheadPath{fill:#333333;}#mermaid-svg-XKrApJd30kq15OBB .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-XKrApJd30kq15OBB .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-XKrApJd30kq15OBB .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-XKrApJd30kq15OBB .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-XKrApJd30kq15OBB .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-XKrApJd30kq15OBB .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-XKrApJd30kq15OBB .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-XKrApJd30kq15OBB .cluster text{fill:#333;}#mermaid-svg-XKrApJd30kq15OBB .cluster span{color:#333;}#mermaid-svg-XKrApJd30kq15OBB div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-XKrApJd30kq15OBB .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-XKrApJd30kq15OBB rect.text{fill:none;stroke-width:0;}#mermaid-svg-XKrApJd30kq15OBB .icon-shape,#mermaid-svg-XKrApJd30kq15OBB .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-XKrApJd30kq15OBB .icon-shape p,#mermaid-svg-XKrApJd30kq15OBB .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-XKrApJd30kq15OBB .icon-shape .label rect,#mermaid-svg-XKrApJd30kq15OBB .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-XKrApJd30kq15OBB .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-XKrApJd30kq15OBB .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-XKrApJd30kq15OBB :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Minor GC
否
是
年龄 >= 15

或大对象
Major/Full GC
新对象
Eden 区
存活?
回收
Survivor 区

年龄 +1
老年代
回收

Minor GC:清理新生代,频繁但速度快。

Major GC / Full GC:清理老年代或整个堆,速度慢,应尽量避免。

对象晋升老年代的条件:

  1. 年龄达到阈值(默认 15,-XX:MaxTenuringThreshold)。
  2. 大对象直接进入老年代(-XX:PretenureSizeThreshold)。
  3. Survivor 中相同年龄对象总和超过一半,该年龄及以上直接晋升。
  4. 动态年龄判定。

7.6 垃圾收集器

收集器 区域 特点 适用场景
Serial 新生代 单线程,STW 客户端、小内存
ParNew 新生代 Serial 多线程版 配合 CMS
Parallel Scavenge 新生代 吞吐量优先 后台计算
Serial Old 老年代 单线程标记-整理 客户端
Parallel Old 老年代 多线程标记-整理 吞吐量优先
CMS 老年代 并发标记清除,低停顿 响应优先(JDK 14 移除)
G1 整堆 分 Region,可预测停顿 JDK 9+ 默认
ZGC 整堆 超低停顿(<1ms),染色指针 大堆、低延迟
Shenandoah 整堆 低停顿,并发整理 低延迟

G1 核心设计 :将堆划分为大小相等的 Region(1~32MB),每个 Region 可动态扮演 Eden、Survivor、Old 角色。通过 -XX:MaxGCPauseMillis 设定目标停顿时间,G1 优先回收价值最高的 Region(Garbage First)。

bash 复制代码
# 启用 G1 并设置目标停顿 200ms
java -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -jar app.jar

ZGC 特点:着色指针 + 读屏障,停顿时间与堆大小无关,支持 TB 级堆。

bash 复制代码
java -XX:+UseZGC -Xmx16g -jar app.jar

7.7 常用 JVM 参数

bash 复制代码
# 堆内存
-Xms2g                          # 初始堆
-Xmx2g                          # 最大堆(建议与初始相同,避免动态扩容)
-Xmn1g                          # 新生代大小
-XX:SurvivorRatio=8             # Eden : Survivor = 8 : 1
-XX:MetaspaceSize=256m          # 元空间初始
-XX:MaxMetaspaceSize=512m       # 元空间上限

# GC 日志(JDK 9+ 统一日志)
-Xlog:gc*:file=gc.log:time,uptime,level,tags

# OOM 时导出堆快照
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/data/dump/

# 收集器
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200

7.8 内存溢出排查

常见 OOM 类型:

  • java.lang.OutOfMemoryError: Java heap space:堆内存不足,多为内存泄漏或大对象。
  • java.lang.OutOfMemoryError: Metaspace:类加载过多,常见于动态代理、热部署。
  • java.lang.OutOfMemoryError: GC overhead limit exceeded:GC 耗时过长但回收效果差。
  • java.lang.OutOfMemoryError: unable to create new native thread:线程数超限。

排查步骤:

  1. 配置 -XX:+HeapDumpOnOutOfMemoryError 自动导出 dump。
  2. 用 MAT(Memory Analyzer Tool)或 JProfiler 打开 dump。
  3. 查看 Dominator Tree,找出占用内存最大的对象。
  4. 查看 Leak Suspects 报告,定位泄漏点。
  5. 结合代码分析引用链。

在线诊断工具:

bash 复制代码
jps                    # 查看 Java 进程
jstat -gc <pid> 1000   # 每秒打印 GC 统计
jmap -heap <pid>       # 堆信息
jmap -dump:format=b,file=heap.hprof <pid>   # 导出堆快照
jstack <pid>           # 线程栈,排查死锁
jinfo -flags <pid>     # 查看 JVM 参数

Arthas 是阿里开源的在线诊断工具,生产环境排查利器:

bash 复制代码
# 启动
java -jar arthas-boot.jar

# 常用命令
dashboard              # 实时面板
thread -n 3            # CPU 占用最高的 3 个线程
thread -b              # 查找死锁
heapdump /tmp/heap.hprof
watch com.example.Service method '{params, returnObj}' -x 2
trace com.example.Service method   # 方法耗时链路

7.9 常见坑

  • -Xms 与 -Xmx 不一致:堆频繁扩容缩容,影响性能。
  • 元空间不设上限:类加载泄漏时可能耗尽机器内存。
  • Full GC 频繁:检查是否有大对象直接进老年代,或 Survivor 过小导致提前晋升。
  • System.gc() 显式调用:会触发 Full GC,生产代码禁止使用。

8. 集合框架

8.1 整体结构

#mermaid-svg-6UBs8XNf3GlZXJmE{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-6UBs8XNf3GlZXJmE .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-6UBs8XNf3GlZXJmE .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-6UBs8XNf3GlZXJmE .error-icon{fill:#552222;}#mermaid-svg-6UBs8XNf3GlZXJmE .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-6UBs8XNf3GlZXJmE .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-6UBs8XNf3GlZXJmE .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-6UBs8XNf3GlZXJmE .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-6UBs8XNf3GlZXJmE .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-6UBs8XNf3GlZXJmE .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-6UBs8XNf3GlZXJmE .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-6UBs8XNf3GlZXJmE .marker{fill:#333333;stroke:#333333;}#mermaid-svg-6UBs8XNf3GlZXJmE .marker.cross{stroke:#333333;}#mermaid-svg-6UBs8XNf3GlZXJmE svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-6UBs8XNf3GlZXJmE p{margin:0;}#mermaid-svg-6UBs8XNf3GlZXJmE .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-6UBs8XNf3GlZXJmE .cluster-label text{fill:#333;}#mermaid-svg-6UBs8XNf3GlZXJmE .cluster-label span{color:#333;}#mermaid-svg-6UBs8XNf3GlZXJmE .cluster-label span p{background-color:transparent;}#mermaid-svg-6UBs8XNf3GlZXJmE .label text,#mermaid-svg-6UBs8XNf3GlZXJmE span{fill:#333;color:#333;}#mermaid-svg-6UBs8XNf3GlZXJmE .node rect,#mermaid-svg-6UBs8XNf3GlZXJmE .node circle,#mermaid-svg-6UBs8XNf3GlZXJmE .node ellipse,#mermaid-svg-6UBs8XNf3GlZXJmE .node polygon,#mermaid-svg-6UBs8XNf3GlZXJmE .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-6UBs8XNf3GlZXJmE .rough-node .label text,#mermaid-svg-6UBs8XNf3GlZXJmE .node .label text,#mermaid-svg-6UBs8XNf3GlZXJmE .image-shape .label,#mermaid-svg-6UBs8XNf3GlZXJmE .icon-shape .label{text-anchor:middle;}#mermaid-svg-6UBs8XNf3GlZXJmE .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-6UBs8XNf3GlZXJmE .rough-node .label,#mermaid-svg-6UBs8XNf3GlZXJmE .node .label,#mermaid-svg-6UBs8XNf3GlZXJmE .image-shape .label,#mermaid-svg-6UBs8XNf3GlZXJmE .icon-shape .label{text-align:center;}#mermaid-svg-6UBs8XNf3GlZXJmE .node.clickable{cursor:pointer;}#mermaid-svg-6UBs8XNf3GlZXJmE .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-6UBs8XNf3GlZXJmE .arrowheadPath{fill:#333333;}#mermaid-svg-6UBs8XNf3GlZXJmE .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-6UBs8XNf3GlZXJmE .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-6UBs8XNf3GlZXJmE .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6UBs8XNf3GlZXJmE .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-6UBs8XNf3GlZXJmE .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6UBs8XNf3GlZXJmE .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-6UBs8XNf3GlZXJmE .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-6UBs8XNf3GlZXJmE .cluster text{fill:#333;}#mermaid-svg-6UBs8XNf3GlZXJmE .cluster span{color:#333;}#mermaid-svg-6UBs8XNf3GlZXJmE div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-6UBs8XNf3GlZXJmE .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-6UBs8XNf3GlZXJmE rect.text{fill:none;stroke-width:0;}#mermaid-svg-6UBs8XNf3GlZXJmE .icon-shape,#mermaid-svg-6UBs8XNf3GlZXJmE .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6UBs8XNf3GlZXJmE .icon-shape p,#mermaid-svg-6UBs8XNf3GlZXJmE .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-6UBs8XNf3GlZXJmE .icon-shape .label rect,#mermaid-svg-6UBs8XNf3GlZXJmE .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6UBs8XNf3GlZXJmE .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-6UBs8XNf3GlZXJmE .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-6UBs8XNf3GlZXJmE :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Iterable
Collection
List

有序可重复
Set

无序不重复
Queue

队列
ArrayList
LinkedList
Vector
HashSet
LinkedHashSet
TreeSet
ArrayDeque
PriorityQueue
Map

键值对
HashMap
LinkedHashMap
TreeMap
ConcurrentHashMap

8.2 ArrayList

基于动态数组实现,随机访问 O(1),中间插入删除 O(n)。

java 复制代码
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add(1, "C");        // 插入到索引 1
list.get(0);             // 随机访问
list.remove("A");
list.size();
list.contains("B");

扩容机制 :默认容量 10,扩容为原容量的 1.5 倍(oldCapacity + (oldCapacity >> 1)),用 Arrays.copyOf 复制。

java 复制代码
// 已知大小时预设容量,避免多次扩容
List<String> list = new ArrayList<>(10000);

遍历删除陷阱:

java 复制代码
// 错误:ConcurrentModificationException
for (String s : list) {
    if (s.equals("A")) list.remove(s);
}

// 正确:用 Iterator
Iterator<String> it = list.iterator();
while (it.hasNext()) {
    if (it.next().equals("A")) it.remove();
}

// 正确:JDK 8+ removeIf
list.removeIf(s -> s.equals("A"));

8.3 LinkedList

基于双向链表,插入删除 O(1)(已知位置),随机访问 O(n)。同时实现了 Deque,可作队列/栈。

java 复制代码
LinkedList<String> list = new LinkedList<>();
list.addFirst("A");
list.addLast("B");
list.removeFirst();
list.peek();       // 查看队首,不删除
list.poll();       // 弹出队首
list.push("C");    // 压栈
list.pop();        // 出栈

ArrayList vs LinkedList:

操作 ArrayList LinkedList
随机访问 get(i) O(1) O(n)
尾部插入 均摊 O(1) O(1)
中间插入 O(n) O(n)(查找)+ O(1)(插入)
内存 连续,紧凑 每个节点额外两个指针

实践中 90% 场景用 ArrayList,LinkedList 仅在频繁头尾操作时有优势。

8.4 HashMap

最常用的 Map 实现,JDK 8 后采用数组 + 链表 + 红黑树结构。

核心参数:

  • 默认初始容量 16。
  • 负载因子 0.75。
  • 链表长度 ≥ 8 且数组长度 ≥ 64 时转红黑树。
  • 扩容为原容量 2 倍。

put 流程:

  1. 计算 key 的 hash:(h = key.hashCode()) ^ (h >>> 16),高低位异或减少碰撞。
  2. 定位桶:(n - 1) & hash。
  3. 桶为空则直接放入。
  4. 桶不为空:key 相同则覆盖;是树节点则插入树;否则遍历链表,尾插,长度达 8 转树。
  5. 判断是否需要扩容。

为什么容量是 2 的幂 :(n - 1) & hash 等价于取模但更快,且 2 的幂保证分布均匀。

为什么负载因子 0.75:空间与时间的折中。太高碰撞多,太低浪费空间。

为什么 JDK 8 改尾插:JDK 7 头插在并发扩容时可能形成环形链表导致死循环。

java 复制代码
Map<String, Integer> map = new HashMap<>();
map.put("a", 1);
map.get("a");
map.getOrDefault("b", 0);
map.putIfAbsent("c", 3);
map.computeIfAbsent("d", k -> k.length());
map.merge("a", 1, Integer::sum);

// 遍历
for (Map.Entry<String, Integer> entry : map.entrySet()) {
    System.out.println(entry.getKey() + " = " + entry.getValue());
}

map.forEach((k, v) -> System.out.println(k + " = " + v));

key 的选择 :必须正确重写 equals 和 hashCode,且 key 不可变(如 String、Integer)。

8.5 LinkedHashMap

继承 HashMap,额外维护双向链表记录插入顺序或访问顺序。

java 复制代码
// 按插入顺序
Map<String, Integer> map = new LinkedHashMap<>();

// 按访问顺序(LRU 基础)
Map<String, Integer> lru = new LinkedHashMap<>(16, 0.75f, true);

实现 LRU 缓存:

java 复制代码
public class LRUCache<K, V> extends LinkedHashMap<K, V> {
    private final int capacity;

    public LRUCache(int capacity) {
        super(capacity, 0.75f, true);
        this.capacity = capacity;
    }

    @Override
    protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
        return size() > capacity;
    }
}

8.6 TreeMap

基于红黑树,key 有序,操作 O(log n)。

java 复制代码
TreeMap<Integer, String> map = new TreeMap<>();
map.put(3, "c");
map.put(1, "a");
map.put(2, "b");
System.out.println(map);              // {1=a, 2=b, 3=c}
System.out.println(map.firstKey());   // 1
System.out.println(map.lastKey());    // 3
System.out.println(map.headMap(2));   // {1=a}
System.out.println(map.tailMap(2));   // {2=b, 3=c}

key 必须实现 Comparable 或传入 Comparator。

8.7 HashSet / TreeSet

HashSet 基于 HashMap 实现,元素作为 key,value 是固定对象。

java 复制代码
Set<String> set = new HashSet<>();
set.add("a");
set.add("a");        // 重复,不加入
set.contains("a");   // true

TreeSet 基于 TreeMap,元素有序。

java 复制代码
Set<Integer> set = new TreeSet<>();
set.add(3);
set.add(1);
set.add(2);
System.out.println(set);   // [1, 2, 3]

8.8 集合遍历与 Stream

java 复制代码
List<String> list = Arrays.asList("a", "b", "c");

// 传统 for
for (int i = 0; i < list.size(); i++) { }

// 增强 for
for (String s : list) { }

// Iterator
Iterator<String> it = list.iterator();
while (it.hasNext()) { it.next(); }

// forEach
list.forEach(System.out::println);

// Stream
list.stream().filter(s -> !s.isEmpty()).forEach(System.out::println);

8.9 集合工具类

java 复制代码
// Collections
List<Integer> list = new ArrayList<>(Arrays.asList(3, 1, 2));
Collections.sort(list);
Collections.reverse(list);
Collections.max(list);
Collections.min(list);
Collections.unmodifiableList(list);   // 不可变视图

// Arrays
int[] arr = {3, 1, 2};
Arrays.sort(arr);
Arrays.binarySearch(arr, 2);
Arrays.asList(1, 2, 3);
Arrays.toString(arr);

// JDK 9+ 不可变集合
List<String> immutable = List.of("a", "b");
Set<String> set = Set.of("a", "b");
Map<String, Integer> map = Map.of("a", 1, "b", 2);

8.10 常见坑

  • Arrays.asList 返回定长列表 :add/remove 抛 UnsupportedOperationException,需 new ArrayList<>(Arrays.asList(...))。
  • HashMap 并发 put :JDK 8 虽不会死循环,但仍会丢数据,并发场景用 ConcurrentHashMap。
  • 遍历中修改集合 :用 Iterator.remove() 或 removeIf。
  • 可变对象作 key:修改后 hashCode 变化,导致查不到。

9. 泛型

9.1 为什么需要泛型

没有泛型时,集合只能存 Object,取出需强转,类型错误在运行时才暴露:

java 复制代码
List list = new ArrayList();
list.add("abc");
list.add(123);
String s = (String) list.get(1);   // 运行时 ClassCastException

泛型把类型检查提前到编译期:

java 复制代码
List<String> list = new ArrayList<>();
list.add("abc");
// list.add(123);   // 编译错误
String s = list.get(0);   // 无需强转

9.2 泛型类与泛型方法

泛型类:

java 复制代码
public class Box<T> {
    private T content;

    public void set(T content) { this.content = content; }
    public T get() { return content; }
}

Box<String> box = new Box<>();
box.set("hello");
String s = box.get();

泛型方法:

java 复制代码
public <T> T getFirst(List<T> list) {
    return list.isEmpty() ? null : list.get(0);
}

// 多类型参数
public <K, V> Map<K, V> pair(K key, V value) {
    Map<K, V> map = new HashMap<>();
    map.put(key, value);
    return map;
}

9.3 类型擦除

Java 泛型是编译期特性 ,编译后类型参数被擦除为边界类型(无边界则为 Object)。

java 复制代码
List<String> a = new ArrayList<>();
List<Integer> b = new ArrayList<>();
System.out.println(a.getClass() == b.getClass());   // true,都是 ArrayList

擦除带来的限制:

  • 不能 new T()。
  • 不能 new T[]。
  • 不能对泛型类型用 instanceof。
  • 静态成员不能使用类类型参数。
  • 不能重载仅类型参数不同的方法。

绕过方式 :通过反射或传入 Class<T>:

java 复制代码
public <T> T create(Class<T> clazz) throws Exception {
    return clazz.getDeclaredConstructor().newInstance();
}

9.4 通配符

上界通配符 ? extends T:只读,可读为 T,不能写。

java 复制代码
public double sum(List<? extends Number> list) {
    double total = 0;
    for (Number n : list) {
        total += n.doubleValue();
    }
    return total;
}

sum(Arrays.asList(1, 2, 3));           // List<Integer>
sum(Arrays.asList(1.1, 2.2));          // List<Double>

下界通配符 ? super T:只写,可写入 T,读出为 Object。

java 复制代码
public void addNumbers(List<? super Integer> list) {
    list.add(1);
    list.add(2);
}

PECS 原则:Producer Extends, Consumer Super。

  • 生产者(只读)用 extends。
  • 消费者(只写)用 super。
  • 既读又写,不用通配符。

9.5 泛型边界

java 复制代码
// 上界:T 必须是 Number 或其子类
public <T extends Number> double sum(List<T> list) {
    double total = 0;
    for (T t : list) total += t.doubleValue();
    return total;
}

// 多边界:第一个可以是类,后面必须是接口
public <T extends Comparable<T> & Serializable> T max(List<T> list) {
    T max = list.get(0);
    for (T t : list) {
        if (t.compareTo(max) > 0) max = t;
    }
    return max;
}

9.6 常见坑

  • 泛型数组创建 :new T[10] 编译错误,用 (T[]) new Object[10] 并加 @SuppressWarnings。
  • 基本类型不能作类型参数 :List<int> 非法,用 List<Integer>。
  • 通配符不能用于返回值 :? extends 读出来是上界类型,写不进去。
  • 静态方法不能用类泛型参数 :需在方法上单独声明 <T>。

10. 多线程与并发控制

10.1 线程的创建

继承 Thread:

java 复制代码
public class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("线程运行:" + Thread.currentThread().getName());
    }
}

new MyThread().start();

实现 Runnable:

java 复制代码
Runnable task = () -> System.out.println("Runnable 运行");
new Thread(task).start();

实现 Callable + FutureTask(可返回结果):

java 复制代码
Callable<Integer> callable = () -> {
    Thread.sleep(1000);
    return 42;
};

FutureTask<Integer> futureTask = new FutureTask<>(callable);
new Thread(futureTask).start();
Integer result = futureTask.get();   // 阻塞等待结果

推荐用线程池 ,而非手动 new Thread。

10.2 线程状态

#mermaid-svg-mo8PplrTPTmLIinW{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-mo8PplrTPTmLIinW .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-mo8PplrTPTmLIinW .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-mo8PplrTPTmLIinW .error-icon{fill:#552222;}#mermaid-svg-mo8PplrTPTmLIinW .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-mo8PplrTPTmLIinW .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-mo8PplrTPTmLIinW .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-mo8PplrTPTmLIinW .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-mo8PplrTPTmLIinW .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-mo8PplrTPTmLIinW .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-mo8PplrTPTmLIinW .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-mo8PplrTPTmLIinW .marker{fill:#333333;stroke:#333333;}#mermaid-svg-mo8PplrTPTmLIinW .marker.cross{stroke:#333333;}#mermaid-svg-mo8PplrTPTmLIinW svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-mo8PplrTPTmLIinW p{margin:0;}#mermaid-svg-mo8PplrTPTmLIinW defs #statediagram-barbEnd{fill:#333333;stroke:#333333;}#mermaid-svg-mo8PplrTPTmLIinW g.stateGroup text{fill:#9370DB;stroke:none;font-size:10px;}#mermaid-svg-mo8PplrTPTmLIinW g.stateGroup text{fill:#333;stroke:none;font-size:10px;}#mermaid-svg-mo8PplrTPTmLIinW g.stateGroup .state-title{font-weight:bolder;fill:#131300;}#mermaid-svg-mo8PplrTPTmLIinW g.stateGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-mo8PplrTPTmLIinW g.stateGroup line{stroke:#333333;stroke-width:1;}#mermaid-svg-mo8PplrTPTmLIinW .transition{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-mo8PplrTPTmLIinW .stateGroup .composit{fill:white;border-bottom:1px;}#mermaid-svg-mo8PplrTPTmLIinW .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px;}#mermaid-svg-mo8PplrTPTmLIinW .state-note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-mo8PplrTPTmLIinW .state-note text{fill:black;stroke:none;font-size:10px;}#mermaid-svg-mo8PplrTPTmLIinW .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-mo8PplrTPTmLIinW .edgeLabel .label rect{fill:#ECECFF;opacity:0.5;}#mermaid-svg-mo8PplrTPTmLIinW .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mo8PplrTPTmLIinW .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-mo8PplrTPTmLIinW .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mo8PplrTPTmLIinW .edgeLabel .label text{fill:#333;}#mermaid-svg-mo8PplrTPTmLIinW .label div .edgeLabel{color:#333;}#mermaid-svg-mo8PplrTPTmLIinW .stateLabel text{fill:#131300;font-size:10px;font-weight:bold;}#mermaid-svg-mo8PplrTPTmLIinW .node circle.state-start{fill:#333333;stroke:#333333;}#mermaid-svg-mo8PplrTPTmLIinW .node .fork-join{fill:#333333;stroke:#333333;}#mermaid-svg-mo8PplrTPTmLIinW .node circle.state-end{fill:#9370DB;stroke:white;stroke-width:1.5;}#mermaid-svg-mo8PplrTPTmLIinW .end-state-inner{fill:white;stroke-width:1.5;}#mermaid-svg-mo8PplrTPTmLIinW .node rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-mo8PplrTPTmLIinW .node polygon{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-mo8PplrTPTmLIinW #statediagram-barbEnd{fill:#333333;}#mermaid-svg-mo8PplrTPTmLIinW .statediagram-cluster rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-mo8PplrTPTmLIinW .cluster-label,#mermaid-svg-mo8PplrTPTmLIinW .nodeLabel{color:#131300;}#mermaid-svg-mo8PplrTPTmLIinW .statediagram-cluster rect.outer{rx:5px;ry:5px;}#mermaid-svg-mo8PplrTPTmLIinW .statediagram-state .divider{stroke:#9370DB;}#mermaid-svg-mo8PplrTPTmLIinW .statediagram-state .title-state{rx:5px;ry:5px;}#mermaid-svg-mo8PplrTPTmLIinW .statediagram-cluster.statediagram-cluster .inner{fill:white;}#mermaid-svg-mo8PplrTPTmLIinW .statediagram-cluster.statediagram-cluster-alt .inner{fill:#f0f0f0;}#mermaid-svg-mo8PplrTPTmLIinW .statediagram-cluster .inner{rx:0;ry:0;}#mermaid-svg-mo8PplrTPTmLIinW .statediagram-state rect.basic{rx:5px;ry:5px;}#mermaid-svg-mo8PplrTPTmLIinW .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#f0f0f0;}#mermaid-svg-mo8PplrTPTmLIinW .note-edge{stroke-dasharray:5;}#mermaid-svg-mo8PplrTPTmLIinW .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-mo8PplrTPTmLIinW .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-mo8PplrTPTmLIinW .statediagram-note text{fill:black;}#mermaid-svg-mo8PplrTPTmLIinW .statediagram-note .nodeLabel{color:black;}#mermaid-svg-mo8PplrTPTmLIinW .statediagram .edgeLabel{color:red;}#mermaid-svg-mo8PplrTPTmLIinW #dependencyStart,#mermaid-svg-mo8PplrTPTmLIinW #dependencyEnd{fill:#333333;stroke:#333333;stroke-width:1;}#mermaid-svg-mo8PplrTPTmLIinW .statediagramTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-mo8PplrTPTmLIinW :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} "new Thread()"
"start()"
"等待 synchronized 锁"
"获取到锁"
"wait() / join() / park()"
"notify() / notifyAll() / unpark()"
"sleep(ms) / wait(ms) / join(ms)"
"超时 / 被唤醒"
"run() 结束"
NEW
RUNNABLE
BLOCKED
WAITING
TIMED_WAITING
TERMINATED

六种状态(Thread.State 枚举):

状态 说明
NEW 创建未启动
RUNNABLE 可运行(含正在运行和就绪)
BLOCKED 等待监视器锁
WAITING 无限等待,需被唤醒
TIMED_WAITING 限时等待
TERMINATED 已终止

sleep vs wait:

维度 sleep wait
所属类 Thread Object
释放锁 否 是
唤醒方式 超时自动 notify/notifyAll
使用前提 任意 必须在 synchronized 块中

10.3 synchronized

三种用法:

java 复制代码
// 1. 修饰实例方法,锁当前对象
public synchronized void method1() { }

// 2. 修饰静态方法,锁 Class 对象
public static synchronized void method2() { }

// 3. 修饰代码块,锁指定对象
public void method3() {
    synchronized (this) {
        // 临界区
    }
}

锁升级过程(JDK 6 后优化):
#mermaid-svg-JmLrbnsf7EGWocfO{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-JmLrbnsf7EGWocfO .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-JmLrbnsf7EGWocfO .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-JmLrbnsf7EGWocfO .error-icon{fill:#552222;}#mermaid-svg-JmLrbnsf7EGWocfO .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-JmLrbnsf7EGWocfO .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-JmLrbnsf7EGWocfO .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-JmLrbnsf7EGWocfO .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-JmLrbnsf7EGWocfO .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-JmLrbnsf7EGWocfO .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-JmLrbnsf7EGWocfO .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-JmLrbnsf7EGWocfO .marker{fill:#333333;stroke:#333333;}#mermaid-svg-JmLrbnsf7EGWocfO .marker.cross{stroke:#333333;}#mermaid-svg-JmLrbnsf7EGWocfO svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-JmLrbnsf7EGWocfO p{margin:0;}#mermaid-svg-JmLrbnsf7EGWocfO .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-JmLrbnsf7EGWocfO .cluster-label text{fill:#333;}#mermaid-svg-JmLrbnsf7EGWocfO .cluster-label span{color:#333;}#mermaid-svg-JmLrbnsf7EGWocfO .cluster-label span p{background-color:transparent;}#mermaid-svg-JmLrbnsf7EGWocfO .label text,#mermaid-svg-JmLrbnsf7EGWocfO span{fill:#333;color:#333;}#mermaid-svg-JmLrbnsf7EGWocfO .node rect,#mermaid-svg-JmLrbnsf7EGWocfO .node circle,#mermaid-svg-JmLrbnsf7EGWocfO .node ellipse,#mermaid-svg-JmLrbnsf7EGWocfO .node polygon,#mermaid-svg-JmLrbnsf7EGWocfO .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-JmLrbnsf7EGWocfO .rough-node .label text,#mermaid-svg-JmLrbnsf7EGWocfO .node .label text,#mermaid-svg-JmLrbnsf7EGWocfO .image-shape .label,#mermaid-svg-JmLrbnsf7EGWocfO .icon-shape .label{text-anchor:middle;}#mermaid-svg-JmLrbnsf7EGWocfO .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-JmLrbnsf7EGWocfO .rough-node .label,#mermaid-svg-JmLrbnsf7EGWocfO .node .label,#mermaid-svg-JmLrbnsf7EGWocfO .image-shape .label,#mermaid-svg-JmLrbnsf7EGWocfO .icon-shape .label{text-align:center;}#mermaid-svg-JmLrbnsf7EGWocfO .node.clickable{cursor:pointer;}#mermaid-svg-JmLrbnsf7EGWocfO .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-JmLrbnsf7EGWocfO .arrowheadPath{fill:#333333;}#mermaid-svg-JmLrbnsf7EGWocfO .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-JmLrbnsf7EGWocfO .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-JmLrbnsf7EGWocfO .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-JmLrbnsf7EGWocfO .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-JmLrbnsf7EGWocfO .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-JmLrbnsf7EGWocfO .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-JmLrbnsf7EGWocfO .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-JmLrbnsf7EGWocfO .cluster text{fill:#333;}#mermaid-svg-JmLrbnsf7EGWocfO .cluster span{color:#333;}#mermaid-svg-JmLrbnsf7EGWocfO div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-JmLrbnsf7EGWocfO .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-JmLrbnsf7EGWocfO rect.text{fill:none;stroke-width:0;}#mermaid-svg-JmLrbnsf7EGWocfO .icon-shape,#mermaid-svg-JmLrbnsf7EGWocfO .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-JmLrbnsf7EGWocfO .icon-shape p,#mermaid-svg-JmLrbnsf7EGWocfO .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-JmLrbnsf7EGWocfO .icon-shape .label rect,#mermaid-svg-JmLrbnsf7EGWocfO .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-JmLrbnsf7EGWocfO .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-JmLrbnsf7EGWocfO .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-JmLrbnsf7EGWocfO :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 无锁
偏向锁

同一线程重入
轻量级锁

CAS 自旋
重量级锁

OS 互斥量

  • 偏向锁:只有一个线程访问时,在 Mark Word 记录线程 ID,后续进入无需 CAS。JDK 15 后默认禁用,JDK 18 移除。
  • 轻量级锁:多线程交替访问,用 CAS 自旋尝试获取。
  • 重量级锁:竞争激烈时,线程阻塞,由操作系统调度。

生产者消费者示例:

java 复制代码
public class Buffer {
    private final Queue<Integer> queue = new LinkedList<>();
    private final int capacity = 10;

    public synchronized void produce(int value) throws InterruptedException {
        while (queue.size() == capacity) {
            wait();   // 队列满,等待
        }
        queue.offer(value);
        System.out.println("生产:" + value);
        notifyAll();
    }

    public synchronized int consume() throws InterruptedException {
        while (queue.isEmpty()) {
            wait();   // 队列空,等待
        }
        int value = queue.poll();
        System.out.println("消费:" + value);
        notifyAll();
        return value;
    }
}

为什么用 while 而非 if:被唤醒后需重新检查条件,防止虚假唤醒。

10.4 volatile

保证可见性 和有序性,不保证原子性。

java 复制代码
public class Flag {
    private volatile boolean running = true;

    public void stop() {
        running = false;
    }

    public void run() {
        while (running) {
            // 若 running 不是 volatile,其他线程修改后本线程可能永远看不到
        }
    }
}

原理:

  • 写操作后插入 StoreLoad 屏障,强制刷新主存。
  • 读操作前插入屏障,强制从主存读取。
  • 禁止指令重排(内存屏障)。

不保证原子性:

java 复制代码
private volatile int count = 0;
public void increment() {
    count++;   // 非原子:读-改-写三步,并发下会丢更新
}

需要原子性用 AtomicInteger 或 synchronized。

双重检查锁单例:

java 复制代码
public class Singleton {
    private static volatile Singleton instance;

    private Singleton() { }

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

volatile 防止 new Singleton() 的指令重排(分配内存 → 初始化 → 赋值引用),避免其他线程拿到未初始化完成的对象。

10.5 Lock 与 ReentrantLock

java 复制代码
private final ReentrantLock lock = new ReentrantLock();

public void method() {
    lock.lock();
    try {
        // 临界区
    } finally {
        lock.unlock();   // 必须在 finally 中释放
    }
}

ReentrantLock 相比 synchronized 的优势:

  • 可中断获取锁:lockInterruptibly()。
  • 可超时:tryLock(1, TimeUnit.SECONDS)。
  • 公平锁:new ReentrantLock(true)。
  • 可绑定多个 Condition。
java 复制代码
ReentrantLock lock = new ReentrantLock();
Condition notFull = lock.newCondition();
Condition notEmpty = lock.newCondition();

// 生产者
lock.lock();
try {
    while (queue.size() == capacity) notFull.await();
    queue.offer(value);
    notEmpty.signalAll();
} finally {
    lock.unlock();
}

10.6 线程池

核心参数:

java 复制代码
ThreadPoolExecutor executor = new ThreadPoolExecutor(
    5,                              // corePoolSize 核心线程数
    10,                             // maximumPoolSize 最大线程数
    60L, TimeUnit.SECONDS,          // keepAliveTime 空闲存活时间
    new ArrayBlockingQueue<>(100),  // workQueue 任务队列
    Executors.defaultThreadFactory(),
    new ThreadPoolExecutor.AbortPolicy()   // 拒绝策略
);

执行流程:
#mermaid-svg-BRe17MxSTCTFWHtI{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-BRe17MxSTCTFWHtI .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-BRe17MxSTCTFWHtI .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-BRe17MxSTCTFWHtI .error-icon{fill:#552222;}#mermaid-svg-BRe17MxSTCTFWHtI .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-BRe17MxSTCTFWHtI .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-BRe17MxSTCTFWHtI .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-BRe17MxSTCTFWHtI .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-BRe17MxSTCTFWHtI .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-BRe17MxSTCTFWHtI .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-BRe17MxSTCTFWHtI .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-BRe17MxSTCTFWHtI .marker{fill:#333333;stroke:#333333;}#mermaid-svg-BRe17MxSTCTFWHtI .marker.cross{stroke:#333333;}#mermaid-svg-BRe17MxSTCTFWHtI svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-BRe17MxSTCTFWHtI p{margin:0;}#mermaid-svg-BRe17MxSTCTFWHtI .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-BRe17MxSTCTFWHtI .cluster-label text{fill:#333;}#mermaid-svg-BRe17MxSTCTFWHtI .cluster-label span{color:#333;}#mermaid-svg-BRe17MxSTCTFWHtI .cluster-label span p{background-color:transparent;}#mermaid-svg-BRe17MxSTCTFWHtI .label text,#mermaid-svg-BRe17MxSTCTFWHtI span{fill:#333;color:#333;}#mermaid-svg-BRe17MxSTCTFWHtI .node rect,#mermaid-svg-BRe17MxSTCTFWHtI .node circle,#mermaid-svg-BRe17MxSTCTFWHtI .node ellipse,#mermaid-svg-BRe17MxSTCTFWHtI .node polygon,#mermaid-svg-BRe17MxSTCTFWHtI .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-BRe17MxSTCTFWHtI .rough-node .label text,#mermaid-svg-BRe17MxSTCTFWHtI .node .label text,#mermaid-svg-BRe17MxSTCTFWHtI .image-shape .label,#mermaid-svg-BRe17MxSTCTFWHtI .icon-shape .label{text-anchor:middle;}#mermaid-svg-BRe17MxSTCTFWHtI .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-BRe17MxSTCTFWHtI .rough-node .label,#mermaid-svg-BRe17MxSTCTFWHtI .node .label,#mermaid-svg-BRe17MxSTCTFWHtI .image-shape .label,#mermaid-svg-BRe17MxSTCTFWHtI .icon-shape .label{text-align:center;}#mermaid-svg-BRe17MxSTCTFWHtI .node.clickable{cursor:pointer;}#mermaid-svg-BRe17MxSTCTFWHtI .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-BRe17MxSTCTFWHtI .arrowheadPath{fill:#333333;}#mermaid-svg-BRe17MxSTCTFWHtI .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-BRe17MxSTCTFWHtI .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-BRe17MxSTCTFWHtI .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-BRe17MxSTCTFWHtI .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-BRe17MxSTCTFWHtI .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-BRe17MxSTCTFWHtI .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-BRe17MxSTCTFWHtI .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-BRe17MxSTCTFWHtI .cluster text{fill:#333;}#mermaid-svg-BRe17MxSTCTFWHtI .cluster span{color:#333;}#mermaid-svg-BRe17MxSTCTFWHtI div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-BRe17MxSTCTFWHtI .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-BRe17MxSTCTFWHtI rect.text{fill:none;stroke-width:0;}#mermaid-svg-BRe17MxSTCTFWHtI .icon-shape,#mermaid-svg-BRe17MxSTCTFWHtI .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-BRe17MxSTCTFWHtI .icon-shape p,#mermaid-svg-BRe17MxSTCTFWHtI .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-BRe17MxSTCTFWHtI .icon-shape .label rect,#mermaid-svg-BRe17MxSTCTFWHtI .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-BRe17MxSTCTFWHtI .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-BRe17MxSTCTFWHtI .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-BRe17MxSTCTFWHtI :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否
是
否
是
否
是
提交任务
核心线程是否已满?
创建核心线程执行
队列是否已满?
放入队列等待
线程数是否达最大?
创建非核心线程执行
执行拒绝策略

四种拒绝策略:

策略 行为
AbortPolicy 抛 RejectedExecutionException(默认)
CallerRunsPolicy 调用线程自己执行
DiscardPolicy 静默丢弃
DiscardOldestPolicy 丢弃队列最老任务,重试提交

Executors 工厂方法的陷阱:

java 复制代码
// 不推荐:队列无界,可能 OOM
Executors.newFixedThreadPool(10);
Executors.newSingleThreadExecutor();

// 不推荐:最大线程数 Integer.MAX_VALUE,可能创建海量线程
Executors.newCachedThreadPool();

// 推荐:手动 new ThreadPoolExecutor,明确参数

线程数设置经验:

  • CPU 密集型:N + 1(N 为 CPU 核数)。
  • I/O 密集型:2N 或 N * (1 + 等待时间/计算时间)。

10.7 死锁

四个必要条件:

  1. 互斥:资源只能被一个线程占用。
  2. 占有且等待:持有资源同时等待其他资源。
  3. 不可剥夺:资源不能被强制抢占。
  4. 循环等待:线程间形成环形等待链。

死锁示例:

java 复制代码
Object lockA = new Object();
Object lockB = new Object();

// 线程 1
new Thread(() -> {
    synchronized (lockA) {
        sleep(100);
        synchronized (lockB) { }
    }
}).start();

// 线程 2
new Thread(() -> {
    synchronized (lockB) {
        sleep(100);
        synchronized (lockA) { }
    }
}).start();

避免策略:

  • 固定加锁顺序:所有线程按同一顺序获取锁。
  • 超时放弃 :用 tryLock(timeout)。
  • 减少锁粒度:避免嵌套锁。
  • 一次性申请所有资源。

检测 :jstack <pid> 会输出 Found one Java-level deadlock,Arthas 的 thread -b 也可直接定位。

10.8 守护线程

守护线程(Daemon Thread)是为其他线程提供服务的后台线程,当所有非守护线程结束时,JVM 自动退出,守护线程被强制终止。

java 复制代码
Thread daemon = new Thread(() -> {
    while (true) {
        System.out.println("守护线程运行中");
        try { Thread.sleep(1000); } catch (InterruptedException e) { break; }
    }
});
daemon.setDaemon(true);   // 必须在 start() 之前设置
daemon.start();

特点:

  • setDaemon(true) 必须在 start() 前调用,否则抛 IllegalThreadStateException。
  • 守护线程中创建的线程默认也是守护线程。
  • 守护线程不能用于执行必须完成的任务(如写文件、提交事务),因为 JVM 退出时会被直接杀死。
  • 典型应用:GC 线程、JIT 编译线程、心跳检测。

验证示例:

java 复制代码
public static void main(String[] args) {
    Thread t = new Thread(() -> {
        try {
            Thread.sleep(10000);
            System.out.println("守护线程执行完毕");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    });
    t.setDaemon(true);
    t.start();

    System.out.println("主线程结束");
    // 主线程结束后 JVM 立即退出,守护线程的 10 秒 sleep 不会执行完
}

10.9 并发工具类

CountDownLatch:等待多个任务完成。

java 复制代码
CountDownLatch latch = new CountDownLatch(3);
for (int i = 0; i < 3; i++) {
    new Thread(() -> {
        try {
            // 执行任务
        } finally {
            latch.countDown();
        }
    }).start();
}
latch.await();   // 等待计数归零

CyclicBarrier:一组线程互相等待到齐后继续,可循环使用。

java 复制代码
CyclicBarrier barrier = new CyclicBarrier(3, () -> System.out.println("全部到齐"));
for (int i = 0; i < 3; i++) {
    new Thread(() -> {
        try {
            barrier.await();
        } catch (Exception e) { }
    }).start();
}

Semaphore:控制并发访问数量。

java 复制代码
Semaphore semaphore = new Semaphore(3);   // 最多 3 个并发

semaphore.acquire();
try {
    // 访问受限资源
} finally {
    semaphore.release();
}

AtomicInteger:CAS 实现的无锁原子类。

java 复制代码
AtomicInteger count = new AtomicInteger(0);
count.incrementAndGet();
count.compareAndSet(1, 2);
count.getAndAdd(5);

10.10 常见坑

  • run() 而非 start() :直接调 run() 是普通方法调用,不会创建新线程。
  • Thread.sleep(0):不是"不睡眠",而是让出 CPU 重新参与调度。
  • 线程池不关闭 :shutdown() 优雅关闭,shutdownNow() 立即中断。
  • SimpleDateFormat 非线程安全 :多线程共享会解析出错,用 DateTimeFormatter。
  • ThreadLocal 内存泄漏 :线程池中线程复用,用完必须 remove()。

11. 反射机制

11.1 什么是反射

反射允许程序在运行时获取类的信息并操作对象,是框架的基石(Spring、MyBatis、JUnit 都重度依赖)。

java 复制代码
Class<?> clazz = Class.forName("com.example.User");
Object obj = clazz.getDeclaredConstructor().newInstance();
Method method = clazz.getMethod("getName");
Object result = method.invoke(obj);

11.2 获取 Class 对象

java 复制代码
// 1. 类名.class
Class<?> c1 = User.class;

// 2. 对象.getClass()
User user = new User();
Class<?> c2 = user.getClass();

// 3. Class.forName(会触发类初始化)
Class<?> c3 = Class.forName("com.example.User");

// 4. 类加载器
Class<?> c4 = ClassLoader.getSystemClassLoader().loadClass("com.example.User");

11.3 常用 API

java 复制代码
Class<?> clazz = User.class;

// 类信息
String name = clazz.getName();              // 全限定名
String simpleName = clazz.getSimpleName();  // 简单名
int modifiers = clazz.getModifiers();       // 修饰符
Package pkg = clazz.getPackage();

// 构造器
Constructor<?>[] constructors = clazz.getConstructors();          // public
Constructor<?>[] declared = clazz.getDeclaredConstructors();      // 全部
Constructor<?> ctor = clazz.getConstructor(String.class, int.class);
Object obj = ctor.newInstance("张三", 25);

// 字段
Field[] fields = clazz.getDeclaredFields();
Field field = clazz.getDeclaredField("name");
field.setAccessible(true);                  // 突破 private
Object value = field.get(obj);
field.set(obj, "李四");

// 方法
Method[] methods = clazz.getDeclaredMethods();
Method method = clazz.getDeclaredMethod("setName", String.class);
method.setAccessible(true);
method.invoke(obj, "王五");

// 注解
Annotation[] annotations = clazz.getAnnotations();
MyAnnotation ann = clazz.getAnnotation(MyAnnotation.class);

11.4 反射操作泛型

java 复制代码
public class GenericDao<T> {
    public void save(List<T> list) { }
}

Method method = GenericDao.class.getMethod("save", List.class);
Type[] types = method.getGenericParameterTypes();
ParameterizedType pt = (ParameterizedType) types[0];
Type actualType = pt.getActualTypeArguments()[0];
System.out.println(actualType);   // T

11.5 反射的性能与优化

反射调用比直接调用慢,原因:

  • 方法查找开销。
  • 参数装箱拆箱。
  • 访问检查。
  • JIT 难以优化。

优化手段:

  • setAccessible(true) 跳过访问检查。
  • 缓存 Method、Field、Constructor 对象。
  • 高频场景用 MethodHandle 或字节码生成(CGLIB、ASM)。
java 复制代码
// 缓存 Method
private static final Map<String, Method> METHOD_CACHE = new ConcurrentHashMap<>();

public Object invokeCached(Object target, String methodName, Object... args) throws Exception {
    Method method = METHOD_CACHE.computeIfAbsent(
        target.getClass().getName() + "#" + methodName,
        k -> {
            try {
                Method m = target.getClass().getMethod(methodName);
                m.setAccessible(true);
                return m;
            } catch (NoSuchMethodException e) {
                throw new RuntimeException(e);
            }
        }
    );
    return method.invoke(target, args);
}

11.6 反射的实际应用

简易 IOC 容器:

java 复制代码
public class SimpleContainer {
    private final Map<Class<?>, Object> beans = new HashMap<>();

    public void register(Class<?> clazz) throws Exception {
        Object instance = clazz.getDeclaredConstructor().newInstance();
        beans.put(clazz, instance);
    }

    public <T> T getBean(Class<T> clazz) {
        return clazz.cast(beans.get(clazz));
    }

    public void inject() throws Exception {
        for (Object bean : beans.values()) {
            for (Field field : bean.getClass().getDeclaredFields()) {
                if (field.isAnnotationPresent(Autowired.class)) {
                    Object dependency = beans.get(field.getType());
                    if (dependency != null) {
                        field.setAccessible(true);
                        field.set(bean, dependency);
                    }
                }
            }
        }
    }
}

11.7 常见坑

  • getMethod 只返回 public :私有方法用 getDeclaredMethod + setAccessible(true)。
  • JDK 17 强封装 :反射访问 JDK 内部 API 会抛 InaccessibleObjectException,需加 --add-opens。
  • 反射破坏单例:私有构造器可被反射调用,枚举单例可防。
  • 性能敏感路径慎用:高频调用应缓存或改用其他方案。

12. Lambda 表达式与函数式接口

12.1 函数式接口

只有一个抽象方法的接口,可用 @FunctionalInterface 标注(非强制,但建议)。

java 复制代码
@FunctionalInterface
public interface Calculator {
    int calculate(int a, int b);
}

Calculator add = (a, b) -> a + b;
Calculator multiply = (a, b) -> a * b;
System.out.println(add.calculate(1, 2));        // 3
System.out.println(multiply.calculate(3, 4));   // 12

JDK 内置四大函数式接口:

接口 签名 用途
Function<T,R> R apply(T t) 转换
Consumer<T> void accept(T t) 消费
Supplier<T> T get() 生产
Predicate<T> boolean test(T t) 判断
java 复制代码
Function<String, Integer> length = String::length;
Consumer<String> printer = System.out::println;
Supplier<LocalDate> today = LocalDate::now;
Predicate<String> isEmpty = String::isEmpty;

其他常用:

  • BiFunction<T,U,R>:双参数转换。
  • UnaryOperator<T>:同类型转换。
  • BinaryOperator<T>:双同类型转换。
  • IntPredicate、LongFunction 等基本类型特化版本,避免装箱。

12.2 Lambda 语法

java 复制代码
// 无参
() -> System.out.println("hello");

// 单参,可省略括号
x -> x * 2;

// 多参
(a, b) -> a + b;

// 带类型
(int a, int b) -> a + b;

// 多行语句
(a, b) -> {
    int sum = a + b;
    return sum;
};

12.3 方法引用

四种形式:

java 复制代码
// 1. 静态方法引用:类::静态方法
Function<Integer, String> f1 = String::valueOf;

// 2. 实例方法引用:对象::实例方法
String str = "hello";
Supplier<Integer> f2 = str::length;

// 3. 类::实例方法(第一个参数作为调用者)
Function<String, Integer> f3 = String::length;

// 4. 构造器引用:类::new
Supplier<ArrayList<String>> f4 = ArrayList::new;
Function<String, User> f5 = User::new;

12.4 变量捕获

Lambda 可访问外部局部变量,但必须是 final 或事实 final(未被重新赋值)。

java 复制代码
int base = 10;
Function<Integer, Integer> add = x -> x + base;
// base = 20;   // 编译错误

// 成员变量无此限制
private int count = 0;
public void test() {
    Runnable r = () -> count++;   // 合法
}

原因:Lambda 编译后局部变量被复制到内部类,若允许修改会导致内外不一致。

12.5 常见坑

  • Lambda 中 this 指向外部类:与匿名内部类不同,Lambda 没有自己的 this。
  • Lambda 不能修改外部局部变量 :需用数组或 AtomicInteger 包装。
  • 函数式接口不能有多个抽象方法:否则无法用 Lambda 实现。
  • 调试困难:Lambda 堆栈信息不直观,复杂逻辑建议抽成方法。

13. Stream API

13.1 什么是 Stream

Stream 是 JDK 8 引入的声明式数据处理 API,支持链式调用、惰性求值、并行处理。

核心特点:

  • 不存储数据:只是对数据源的视图。
  • 不修改源:每次操作返回新 Stream。
  • 惰性求值:中间操作不执行,遇到终止操作才触发。
  • 只能消费一次:Stream 用完即关闭。

13.2 创建 Stream

java 复制代码
// 集合
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> s1 = list.stream();
Stream<String> s2 = list.parallelStream();

// 数组
Stream<String> s3 = Arrays.stream(new String[]{"a", "b"});

// Stream.of
Stream<String> s4 = Stream.of("a", "b", "c");

// 无限流
Stream<Integer> s5 = Stream.iterate(0, n -> n + 2).limit(10);
Stream<Double> s6 = Stream.generate(Math::random).limit(5);

// 数值流
IntStream.range(1, 10);
IntStream.rangeClosed(1, 10);

13.3 中间操作

filter:过滤。

java 复制代码
list.stream().filter(s -> s.startsWith("a")).collect(Collectors.toList());

map:转换。

java 复制代码
list.stream().map(String::toUpperCase).collect(Collectors.toList());

flatMap:扁平化。

java 复制代码
List<List<Integer>> nested = Arrays.asList(Arrays.asList(1, 2), Arrays.asList(3, 4));
List<Integer> flat = nested.stream()
    .flatMap(List::stream)
    .collect(Collectors.toList());   // [1, 2, 3, 4]

distinct:去重(依赖 equals/hashCode)。

java 复制代码
list.stream().distinct().collect(Collectors.toList());

sorted:排序。

java 复制代码
list.stream().sorted().collect(Collectors.toList());
list.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList());
list.stream().sorted(Comparator.comparing(User::getAge)).collect(Collectors.toList());

limit / skip:分页。

java 复制代码
list.stream().skip(10).limit(10).collect(Collectors.toList());

peek:调试用,不改变元素。

java 复制代码
list.stream().peek(System.out::println).collect(Collectors.toList());

13.4 终止操作

collect:收集。

java 复制代码
List<String> result = list.stream().collect(Collectors.toList());
Set<String> set = list.stream().collect(Collectors.toSet());
String joined = list.stream().collect(Collectors.joining(", "));
Map<String, Integer> map = list.stream()
    .collect(Collectors.toMap(s -> s, String::length));

forEach:遍历。

java 复制代码
list.stream().forEach(System.out::println);

reduce:归约。

java 复制代码
Optional<Integer> sum = Stream.of(1, 2, 3, 4).reduce(Integer::sum);
int total = Stream.of(1, 2, 3, 4).reduce(0, Integer::sum);

count / min / max:

java 复制代码
long count = list.stream().count();
Optional<String> min = list.stream().min(String::compareTo);
Optional<Integer> max = Stream.of(1, 2, 3).max(Integer::compareTo);

anyMatch / allMatch / noneMatch:

java 复制代码
boolean any = list.stream().anyMatch(s -> s.startsWith("a"));
boolean all = list.stream().allMatch(s -> s.length() > 0);
boolean none = list.stream().noneMatch(String::isEmpty);

findFirst / findAny:

java 复制代码
Optional<String> first = list.stream().findFirst();
Optional<String> any = list.parallelStream().findAny();

13.5 Collectors 高级用法

分组:

java 复制代码
Map<String, List<User>> byCity = users.stream()
    .collect(Collectors.groupingBy(User::getCity));

// 分组后计数
Map<String, Long> countByCity = users.stream()
    .collect(Collectors.groupingBy(User::getCity, Collectors.counting()));

// 多级分组
Map<String, Map<Integer, List<User>>> multi = users.stream()
    .collect(Collectors.groupingBy(User::getCity,
             Collectors.groupingBy(User::getAge)));

分区:按布尔条件分两组。

java 复制代码
Map<Boolean, List<User>> partition = users.stream()
    .collect(Collectors.partitioningBy(u -> u.getAge() >= 18));

统计:

java 复制代码
IntSummaryStatistics stats = users.stream()
    .collect(Collectors.summarizingInt(User::getAge));
System.out.println(stats.getMax());
System.out.println(stats.getAverage());
System.out.println(stats.getSum());

映射:

java 复制代码
Map<Long, String> idToName = users.stream()
    .collect(Collectors.toMap(User::getId, User::getName));

// 处理 key 冲突
Map<String, User> byName = users.stream()
    .collect(Collectors.toMap(User::getName, u -> u, (a, b) -> a));

13.6 并行流

java 复制代码
long count = list.parallelStream()
    .filter(s -> s.length() > 3)
    .count();

并行流的陷阱:

  • 线程安全 :共享可变状态会出错,用 collect 而非 forEach 累加。
  • 顺序问题 :findFirst 在并行流中开销大,用 findAny。
  • 数据量小反而更慢:线程调度开销大于收益。
  • 底层用 ForkJoinPool.commonPool:默认线程数为 CPU 核数 - 1,I/O 密集任务会阻塞。
java 复制代码
// 错误:非线程安全
List<Integer> result = new ArrayList<>();
IntStream.range(0, 1000).parallel().forEach(result::add);

// 正确
List<Integer> safe = IntStream.range(0, 1000).parallel()
    .boxed()
    .collect(Collectors.toList());

13.7 实战案例

案例 1:订单统计

java 复制代码
public class OrderStats {
    public static void main(String[] args) {
        List<Order> orders = getOrders();

        // 总金额
        BigDecimal total = orders.stream()
            .map(Order::getAmount)
            .reduce(BigDecimal.ZERO, BigDecimal::add);

        // 按用户分组统计
        Map<Long, BigDecimal> byUser = orders.stream()
            .collect(Collectors.groupingBy(
                Order::getUserId,
                Collectors.reducing(BigDecimal.ZERO, Order::getAmount, BigDecimal::add)
            ));

        // 金额最高的前 10 笔
        List<Order> top10 = orders.stream()
            .sorted(Comparator.comparing(Order::getAmount).reversed())
            .limit(10)
            .collect(Collectors.toList());

        // 状态分布
        Map<OrderStatus, Long> statusCount = orders.stream()
            .collect(Collectors.groupingBy(Order::getStatus, Collectors.counting()));
    }
}

案例 2:列表转树

java 复制代码
public List<MenuNode> buildTree(List<Menu> menus) {
    Map<Long, List<Menu>> childrenMap = menus.stream()
        .filter(m -> m.getParentId() != null)
        .collect(Collectors.groupingBy(Menu::getParentId));

    return menus.stream()
        .filter(m -> m.getParentId() == null)
        .map(m -> convert(m, childrenMap))
        .collect(Collectors.toList());
}

private MenuNode convert(Menu menu, Map<Long, List<Menu>> childrenMap) {
    MenuNode node = new MenuNode(menu);
    List<Menu> children = childrenMap.getOrDefault(menu.getId(), Collections.emptyList());
    node.setChildren(children.stream()
        .map(c -> convert(c, childrenMap))
        .collect(Collectors.toList()));
    return node;
}

案例 3:字符串处理

java 复制代码
String text = "hello world java stream";

// 词频统计
Map<String, Long> wordCount = Arrays.stream(text.split("\\s+"))
    .collect(Collectors.groupingBy(s -> s, Collectors.counting()));

// 首字母大写
String result = Arrays.stream(text.split("\\s+"))
    .map(s -> s.substring(0, 1).toUpperCase() + s.substring(1))
    .collect(Collectors.joining(" "));

13.8 常见坑

  • Stream 重复使用 :IllegalStateException: stream has already been operated upon or closed。
  • 惰性求值误解:没有终止操作,中间操作不会执行。
  • Collectors.toMap 重复 key :抛 IllegalStateException,需传合并函数。
  • toMap value 为 null :抛 NPE,用 HashMap 手动收集或过滤。
  • 并行流中修改共享集合:结果不可预期。

14. 综合实战:一个完整的并发场景

把前面知识串起来,实现一个"高并发下安全的本地缓存":

java 复制代码
public class LocalCache<K, V> {
    private final ConcurrentHashMap<K, V> cache = new ConcurrentHashMap<>();
    private final ConcurrentHashMap<K, Long> expireTime = new ConcurrentHashMap<>();
    private final long ttlMillis;

    public LocalCache(long ttlMillis) {
        this.ttlMillis = ttlMillis;
    }

    public V get(K key, Function<K, V> loader) {
        Long expire = expireTime.get(key);
        if (expire != null && System.currentTimeMillis() > expire) {
            cache.remove(key);
            expireTime.remove(key);
        }
        return cache.computeIfAbsent(key, k -> {
            V value = loader.apply(k);
            expireTime.put(k, System.currentTimeMillis() + ttlMillis);
            return value;
        });
    }

    public void put(K key, V value) {
        cache.put(key, value);
        expireTime.put(key, System.currentTimeMillis() + ttlMillis);
    }

    public void remove(K key) {
        cache.remove(key);
        expireTime.remove(key);
    }

    public int size() {
        return cache.size();
    }
}

这个例子用到了:

  • ConcurrentHashMap 的线程安全与 computeIfAbsent 原子性(第 8 章)。
  • Function 函数式接口(第 12 章)。
  • 并发场景下的可见性与原子性考量(第 10 章)。
  • 泛型设计(第 9 章)。

15. 学习路线与总结

15.1 知识地图

#mermaid-svg-47kJKOLlgXvMI5jX{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-47kJKOLlgXvMI5jX .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-47kJKOLlgXvMI5jX .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-47kJKOLlgXvMI5jX .error-icon{fill:#552222;}#mermaid-svg-47kJKOLlgXvMI5jX .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-47kJKOLlgXvMI5jX .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-47kJKOLlgXvMI5jX .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-47kJKOLlgXvMI5jX .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-47kJKOLlgXvMI5jX .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-47kJKOLlgXvMI5jX .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-47kJKOLlgXvMI5jX .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-47kJKOLlgXvMI5jX .marker{fill:#333333;stroke:#333333;}#mermaid-svg-47kJKOLlgXvMI5jX .marker.cross{stroke:#333333;}#mermaid-svg-47kJKOLlgXvMI5jX svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-47kJKOLlgXvMI5jX p{margin:0;}#mermaid-svg-47kJKOLlgXvMI5jX .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-47kJKOLlgXvMI5jX .cluster-label text{fill:#333;}#mermaid-svg-47kJKOLlgXvMI5jX .cluster-label span{color:#333;}#mermaid-svg-47kJKOLlgXvMI5jX .cluster-label span p{background-color:transparent;}#mermaid-svg-47kJKOLlgXvMI5jX .label text,#mermaid-svg-47kJKOLlgXvMI5jX span{fill:#333;color:#333;}#mermaid-svg-47kJKOLlgXvMI5jX .node rect,#mermaid-svg-47kJKOLlgXvMI5jX .node circle,#mermaid-svg-47kJKOLlgXvMI5jX .node ellipse,#mermaid-svg-47kJKOLlgXvMI5jX .node polygon,#mermaid-svg-47kJKOLlgXvMI5jX .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-47kJKOLlgXvMI5jX .rough-node .label text,#mermaid-svg-47kJKOLlgXvMI5jX .node .label text,#mermaid-svg-47kJKOLlgXvMI5jX .image-shape .label,#mermaid-svg-47kJKOLlgXvMI5jX .icon-shape .label{text-anchor:middle;}#mermaid-svg-47kJKOLlgXvMI5jX .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-47kJKOLlgXvMI5jX .rough-node .label,#mermaid-svg-47kJKOLlgXvMI5jX .node .label,#mermaid-svg-47kJKOLlgXvMI5jX .image-shape .label,#mermaid-svg-47kJKOLlgXvMI5jX .icon-shape .label{text-align:center;}#mermaid-svg-47kJKOLlgXvMI5jX .node.clickable{cursor:pointer;}#mermaid-svg-47kJKOLlgXvMI5jX .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-47kJKOLlgXvMI5jX .arrowheadPath{fill:#333333;}#mermaid-svg-47kJKOLlgXvMI5jX .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-47kJKOLlgXvMI5jX .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-47kJKOLlgXvMI5jX .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-47kJKOLlgXvMI5jX .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-47kJKOLlgXvMI5jX .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-47kJKOLlgXvMI5jX .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-47kJKOLlgXvMI5jX .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-47kJKOLlgXvMI5jX .cluster text{fill:#333;}#mermaid-svg-47kJKOLlgXvMI5jX .cluster span{color:#333;}#mermaid-svg-47kJKOLlgXvMI5jX div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-47kJKOLlgXvMI5jX .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-47kJKOLlgXvMI5jX rect.text{fill:none;stroke-width:0;}#mermaid-svg-47kJKOLlgXvMI5jX .icon-shape,#mermaid-svg-47kJKOLlgXvMI5jX .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-47kJKOLlgXvMI5jX .icon-shape p,#mermaid-svg-47kJKOLlgXvMI5jX .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-47kJKOLlgXvMI5jX .icon-shape .label rect,#mermaid-svg-47kJKOLlgXvMI5jX .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-47kJKOLlgXvMI5jX .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-47kJKOLlgXvMI5jX .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-47kJKOLlgXvMI5jX :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Java 知识体系
工具层

IDEA / Maven / Git
语言层

语法 / OOP / 异常 / I/O
核心库层

集合 / 泛型 / 并发 / 反射 / Stream
运行时层

JVM / GC / 调优
框架层

Spring / MyBatis / Netty

15.2 分阶段学习建议

第一阶段(1~2 个月):语法、面向对象、异常、I/O。目标是能独立写出结构清晰的小程序。

第二阶段(2~3 个月):集合、泛型、多线程、反射、Stream。目标是理解 JDK 核心 API 的设计思想,能读懂框架源码。

第三阶段(持续):JVM 调优、并发编程深入(AQS、CAS、内存模型)、设计模式、框架源码。目标是具备排查线上问题和架构设计能力。

15.3 推荐资源

  • 书籍:《Java 核心技术》《Effective Java》《深入理解 Java 虚拟机》《Java 并发编程实战》。
  • 源码 :JDK 的 java.util、java.util.concurrent 包。
  • 实践:LeetCode 用 Java 刷题,GitHub 上读 Spring、Netty 源码。

15.4 最后的建议

Java 的知识点看似零散,实则有一条主线:从"如何描述数据"(类型、对象)到"如何组织逻辑"(方法、类、接口),再到"如何高效执行"(集合、并发、JVM)。把这条主线想清楚,零散的知识点自然会归位。

不要追求"背完所有 API",而要理解为什么这样设计 。当你明白 HashMap 为什么用 2 的幂做容量、volatile 为什么不能保证原子性、Stream 为什么惰性求值,你就真正掌握了 Java,而不是记住了 Java。

写代码时多问三个问题:这段代码在并发下安全吗?内存占用合理吗?异常路径处理了吗?这三个问题能帮你避开 80% 的生产事故。

相关推荐
文丑颜不良啊4 小时前
LeetCode2761~2780题目解析
java·leetcode
jyOverQ5 小时前
从零学 JVM(六):垃圾回收算法详解——标记清除、标记复制与标记整理
jvm·算法
IT枫斗者枫哥5 小时前
子线程断言失败,JUnit 为什么还显示通过?
java·后端
考虑考虑7 小时前
JDK27中的Set.ofLazy
java·后端·java ee
文丑颜不良啊7 小时前
LeetCode2841~2860题目解析
java·leetcode
小南家的青蛙7 小时前
Tomcat与Jetty深度对比
java·tomcat·jetty
乌暮9 小时前
Spring AOP 与 Spring 事务:用大白话彻底理解原理与实践
java·数据库·spring boot·后端·sql·学习·spring
YDS8299 小时前
Small Spring IOC篇:容器事件及其监听器
java·spring
金銀銅鐵9 小时前
带您看懂字节码指令(三):一行加法里有什么
java·jvm