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();
对象创建过程:
- 检查类是否已加载,未加载则先加载。
- 在堆中分配内存。
- 成员变量赋默认值(0/null/false)。
- 执行构造器,赋初始值。
- 返回对象引用。
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(); // 咪咪 在吃鱼
多态的三个必要条件:
- 有继承或实现关系。
- 子类重写父类方法。
- 父类引用指向子类对象。
动态绑定原理: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 异常处理最佳实践
- 不要吞异常 :
catch (Exception e) {}是反模式,至少记录日志。 - 不要用异常控制流程:异常开销大,正常分支用 if 判断。
- 捕获具体异常:先捕获子类,再捕获父类,否则编译报错。
- 保留原始异常 :包装时传
cause,否则堆栈信息丢失。 - finally 中不要 return:会覆盖 try 中的返回值,且吞掉异常。
- 日志要带上下文:记录关键参数,便于排查。
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();
执行过程:
- 检查常量池是否有
User的符号引用,未加载则触发类加载。 - 在堆中分配内存(指针碰撞或空闲列表)。
- 内存空间初始化为零值。
- 设置对象头(Mark Word、类型指针)。
- 执行构造器。
对象内存布局:
- 对象头: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:清理老年代或整个堆,速度慢,应尽量避免。
对象晋升老年代的条件:
- 年龄达到阈值(默认 15,
-XX:MaxTenuringThreshold)。 - 大对象直接进入老年代(
-XX:PretenureSizeThreshold)。 - Survivor 中相同年龄对象总和超过一半,该年龄及以上直接晋升。
- 动态年龄判定。
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:线程数超限。
排查步骤:
- 配置
-XX:+HeapDumpOnOutOfMemoryError自动导出 dump。 - 用 MAT(Memory Analyzer Tool)或 JProfiler 打开 dump。
- 查看 Dominator Tree,找出占用内存最大的对象。
- 查看 Leak Suspects 报告,定位泄漏点。
- 结合代码分析引用链。
在线诊断工具:
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 流程:
- 计算 key 的 hash:
(h = key.hashCode()) ^ (h >>> 16),高低位异或减少碰撞。 - 定位桶:
(n - 1) & hash。 - 桶为空则直接放入。
- 桶不为空:key 相同则覆盖;是树节点则插入树;否则遍历链表,尾插,长度达 8 转树。
- 判断是否需要扩容。
为什么容量是 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 死锁
四个必要条件:
- 互斥:资源只能被一个线程占用。
- 占有且等待:持有资源同时等待其他资源。
- 不可剥夺:资源不能被强制抢占。
- 循环等待:线程间形成环形等待链。
死锁示例:
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,需传合并函数。toMapvalue 为 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% 的生产事故。