Java 完整手册

一、Java 概述与环境

1.1 Java 是什么

Java 是由 Sun Microsystems(现 Oracle)于 1995 年推出的面向对象编程语言。

核心设计理念:Write Once, Run Anywhere(一次编写,到处运行)——通过 JVM 实现跨平台。

1.2 三大平台版本

版本 全称 用途
Java SE Standard Edition 标准版,桌面应用、基础开发,是其他版本的基础
Java EE Enterprise Edition 企业版,Web 应用、分布式系统(现移交 Eclipse 基金会,改名 Jakarta EE)
Java ME Micro Edition 微型版,嵌入式设备、移动设备(已式微)

1.3 JDK、JRE、JVM 的关系

JDK(Java Development Kit)—— 开发工具包
├── 开发工具(javac、java、jar、javadoc 等)
└── JRE(Java Runtime Environment)—— 运行环境
    ├── 类库(rt.jar / modules)
    └── JVM(Java Virtual Machine)—— 虚拟机
        └── 执行 Java 字节码
组件 包含 用途
JVM 虚拟机 执行字节码,实现跨平台
JRE JVM + 类库 运行 Java 程序
JDK JRE + 开发工具 开发 + 运行 Java 程序

开发用 JDK,部署用 JRE。

JDK bin 目录共 35 个工具详解(编译运行、字节码分析、JVM 监控诊断、安全证书、打包分发、交互式开发等),见 JDK 内置工具详解

1.4 安装与环境配置

环境变量

变量 说明
JAVA_HOME JDK 安装目录
PATH 追加 %JAVA_HOME%\bin(Windows)或 $JAVA_HOME/bin(Linux/Mac)
CLASSPATH 类搜索路径(Java 5+ 一般不需要手动配置,默认当前目录)

验证安装

java -version    # 查看 Java 版本
javac -version   # 查看编译器版本

1.5 第一个 Java 程序

// HelloWorld.java
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, World!");
    }
}

编译运行

javac HelloWorld.java   # 编译,生成 HelloWorld.class
java HelloWorld         # 运行,输出 Hello, World!

要点
- 文件名必须与 public 类名完全一致(大小写敏感)
- main 方法是程序入口,签名固定:public static void main(String[] args)
- Java 严格区分大小写


二、基础语法

2.1 标识符与关键字

标识符命名规则

  • 由字母、数字、下划线(_)、美元符号($)组成
  • 不能以数字开头
  • 不能是 Java 关键字
  • 大小写敏感

命名规范(约定俗成)

类型 规范 举例
类/接口 大驼峰(PascalCase) HelloWorldList
方法/变量 小驼峰(camelCase) getNameuserName
常量 全大写 + 下划线 MAX_SIZEPI
全小写,反向域名 com.example.project

关键字(50 个)

abstract   assert     boolean    break      byte
case       catch      char       class      const
continue   default    do         double     else
enum       extends    final      finally    float
for        goto       if         implements import
instanceof int        interface  long       native
new        package    private    protected  public
return     short      static     strictfp   super
switch     synchronized this       throw      throws
transient  try        void       volatile   while

gotoconst 是保留关键字,目前未使用。

2.2 数据类型

Java 是强类型语言——每个变量都必须声明类型。

基本数据类型(8 种)

类型 大小 默认值 取值范围 包装类
byte 1 字节 0 -128 ~ 127 Byte
short 2 字节 0 -32768 ~ 32767 Short
int 4 字节 0 -2^31 ~ 2^31-1 Integer
long 8 字节 0L -2^63 ~ 2^63-1 Long
float 4 字节 0.0f 约 ±3.4e38 Float
double 8 字节 0.0d 约 ±1.8e308 Double
char 2 字节 '\u0000' '\u0000' ~ '\uffff' Character
boolean 1 位 false true / false Boolean

注意
- 整数默认是 int,long 类型后缀加 Ll(推荐大写 L)
- 浮点数默认是 double,float 类型后缀加 Ff
- char 是 Unicode 字符,可以存中文:char c = '中';
- boolean 只有 true 和 false,不能用 0/1 代替

引用数据类型

  • 类(Class)
  • 接口(Interface)
  • 数组(Array)
  • 枚举(Enum)
  • 记录(Record,Java 16+)

引用类型的默认值是 null

基本类型 vs 引用类型

对比项 基本类型 引用类型
存储位置 栈(局部变量) 栈存引用,堆存对象
大小 固定大小 引用固定大小,对象大小不定
默认值 0 / 0.0 / false / '\u0000' null
比较 == 比较值 == 比较引用地址,equals 比较内容
赋值 直接复制值 复制引用,指向同一对象

关于数据类型系统的深度理解(硬件为什么没有类型、类型的本质定义、静态/动态/强/弱四大象限、基本类型 vs 引用类型的内存布局、类型系统五代演进),见 数据类型系统

2.3 变量与常量

变量声明

// 声明 + 赋值
int age = 25;

// 先声明,后赋值
String name;
name = "张三";

// 同时声明多个变量
int a = 1, b = 2, c = 3;

变量作用域

作用域 说明 默认值
类变量(静态变量) static 修饰,属于类 有默认值
实例变量 属于对象,在类中方法外 有默认值
局部变量 方法内或代码块内 无默认值,必须初始化才能用
public class Example {
    static int classVar = 1;    // 类变量
    int instanceVar = 2;        // 实例变量
    
    void method() {
        int localVar = 3;       // 局部变量
    }
}

常量

final 关键字声明,只能赋值一次:

final double PI = 3.14159;
// PI = 3.14;  // 编译错误,不能重新赋值

// 静态常量(最常用)
public static final int MAX_SIZE = 100;

2.4 运算符

算术运算符

运算符 说明 举例
+ 加 / 字符串连接 5 + 3 = 8"a" + "b" = "ab"
- 5 - 3 = 2
* 5 * 3 = 15
/ 除(整数除法取整) 5 / 2 = 25.0 / 2 = 2.5
% 取模(取余数) 5 % 2 = 1
++ 自增 i++(先用后加)、++i(先加后用)
-- 自减 i----i

赋值运算符

运算符 说明 举例
= 赋值 a = 10
+= 加后赋值 a += 5a = a + 5
-= 减后赋值 a -= 5a = a - 5
*= 乘后赋值 a *= 5a = a * 5
/= 除后赋值 a /= 5a = a / 5
%= 取模后赋值 a %= 5a = a % 5

比较运算符

运算符 说明 举例
== 等于 5 == 3 → false
!= 不等于 5 != 3 → true
> 大于 5 > 3 → true
< 小于 5 < 3 → false
>= 大于等于 5 >= 5 → true
<= 小于等于 5 <= 3 → false

基本类型 == 比较值,引用类型 == 比较地址。

逻辑运算符

运算符 说明 特点
&& 短路与 左边为 false 则不计算右边
\|\| 短路或 左边为 true 则不计算右边
! 取反
& 非短路与 两边都计算
\| 非短路或 两边都计算
^ 异或 两边不同为 true

优先使用短路运算符(&&||),效率更高。

位运算符

运算符 说明 举例
& 按位与 6 & 3 = 2(110 & 011 = 010)
\| 按位或 6 \| 3 = 7(110 | 011 = 111)
^ 按位异或 6 ^ 3 = 5(110 ^ 011 = 101)
~ 按位取反 ~6 = -7
<< 左移 6 << 1 = 12(相当于乘 2)
>> 右移(带符号) 6 >> 1 = 3(相当于除 2)
>>> 无符号右移 高位补 0

三元运算符

// 条件 ? 表达式1 : 表达式2
// 条件为 true 取表达式1,为 false 取表达式2
int max = (a > b) ? a : b;

运算符优先级(从高到低)

1.  ()  []  .
2.  ++  --  !  ~
3.  *  /  %
4.  +  -
5.  <<  >>  >>>
6.  <  <=  >  >=  instanceof
7.  ==  !=
8.  &
9.  ^
10. |
11. &&
12. ||
13. ?:
14. =  +=  -=  *=  /=  %=  ...

不确定优先级就加括号,可读性比炫技重要。

2.5 类型转换

自动类型转换(隐式)

小范围 → 大范围,自动完成,无精度损失:

byte → short → int → long → float → double
         char ↗
int i = 100;
long l = i;      // 自动转换
double d = i;    // 自动转换

强制类型转换(显式)

大范围 → 小范围,可能损失精度,需要强转:

double d = 3.14;
int i = (int) d;   // 强制转换,i = 3(小数部分丢失)

long l = 100L;
int i = (int) l;   // 强制转换

注意:boolean 类型不能与其他类型转换。

2.6 控制流语句

if-else 条件语句

if (条件1) {
    // 条件1 为 true 执行
} else if (条件2) {
    // 条件2 为 true 执行
} else {
    // 都不满足执行
}

switch 选择语句

// 传统 switch
switch (表达式) {
    case 值1:
        // 语句
        break;  // 不加 break 会穿透
    case 值2:
        // 语句
        break;
    default:
        // 默认
        break;
}

// Java 14+:switch 表达式(箭头语法,不需要 break)
String result = switch (day) {
    case 1, 2, 3, 4, 5 -> "工作日";
    case 6, 7 -> "周末";
    default -> "无效";
};

// Java 17+:模式匹配 switch(预览→正式)
String format(Object o) {
    return switch (o) {
        case Integer i -> "int: " + i;
        case String s -> "string: " + s;
        case null -> "null";
        default -> "unknown";
    };
}

for 循环

// 普通 for
for (初始化; 条件; 更新) {
    // 循环体
}

// 增强 for(for-each,Java 5+)
for (类型 变量 : 数组/集合) {
    // 循环体
}

// 示例
int[] arr = {1, 2, 3, 4, 5};
for (int i = 0; i < arr.length; i++) {
    System.out.println(arr[i]);
}

for (int num : arr) {
    System.out.println(num);
}

while 循环

// while:先判断后执行
while (条件) {
    // 循环体
}

// do-while:先执行后判断,至少执行一次
do {
    // 循环体
} while (条件);

跳转语句

语句 说明
break 跳出当前循环或 switch
continue 跳过本次循环,继续下一次
return 结束方法,返回值

不推荐使用带标签的 break/continue(类似 goto),影响可读性。

2.7 数组

一维数组

// 声明
int[] arr1;           // 推荐写法
int arr2[];           // C 风格,不推荐

// 创建
int[] arr = new int[5];           // 指定长度,默认值 0
int[] arr = {1, 2, 3, 4, 5};     // 静态初始化
int[] arr = new int[]{1, 2, 3};  // 完整写法

// 访问
arr[0] = 10;       // 赋值
int x = arr[0];    // 取值
int len = arr.length;  // 数组长度

二维数组

// 声明与创建
int[][] matrix = new int[3][4];     // 3 行 4 列
int[][] matrix = {{1,2,3}, {4,5,6}}; // 静态初始化

// 访问
matrix[0][0] = 1;

// 遍历
for (int i = 0; i < matrix.length; i++) {
    for (int j = 0; j < matrix[i].length; j++) {
        System.out.print(matrix[i][j] + " ");
    }
}

数组工具类:Arrays

import java.util.Arrays;

int[] arr = {3, 1, 4, 1, 5};

Arrays.sort(arr);                    // 排序
Arrays.binarySearch(arr, 4);         // 二分查找(需先排序)
Arrays.fill(arr, 0);                 // 填充
Arrays.equals(arr1, arr2);           // 比较
Arrays.toString(arr);                // 转字符串
Arrays.copyOf(arr, 10);              // 复制
Arrays.copyOfRange(arr, 1, 4);       // 范围复制

三、面向对象编程

3.1 类与对象

类的定义

[修饰符] class 类名 {
    // 字段(属性)
    [修饰符] 类型 字段名;
    
    // 方法(行为)
    [修饰符] 返回类型 方法名(参数列表) {
        // 方法体
    }
}

对象的创建与使用

// 创建对象
类名 对象名 = new 类名();

// 访问成员
对象名.字段名;
对象名.方法名(参数);

构造方法

  • 方法名与类名相同
  • 没有返回值类型(连 void 都没有)
  • 创建对象时自动调用
  • 可以重载
public class Person {
    private String name;
    private int age;
    
    // 无参构造
    public Person() {
    }
    
    // 有参构造
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

// 使用
Person p1 = new Person();           // 调用无参构造
Person p2 = new Person("张三", 25); // 调用有参构造

如果没有写任何构造方法,编译器会自动生成一个无参构造。
如果写了有参构造,就不会自动生成无参构造了。

this 关键字

this 指代当前对象:

  1. 调用本类的属性this.name = name;
  2. 调用本类的方法this.method();
  3. 调用本类的构造方法this(参数);(必须在构造方法第一行)

3.2 封装

访问修饰符

修饰符 同类 同包 子类 全局
private
default(包级私有)
protected
public

封装的标准实现

public class Person {
    // 1. 属性私有
    private String name;
    private int age;
    
    // 2. 提供公共的 getter/setter
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    public int getAge() {
        return age;
    }
    
    public void setAge(int age) {
        if (age < 0 || age > 150) {
            throw new IllegalArgumentException("年龄不合法");
        }
        this.age = age;
    }
}

3.3 继承

基本语法

[修饰符] class 子类名 extends 父类名 {
    // 子类新增的成员
}
  • Java 是单继承:一个类只能继承一个父类
  • 子类继承父类所有非 private 的成员
  • 构造方法不能继承,子类必须调用父类构造方法

super 关键字

super 指代父类对象:

  1. 调用父类的属性super.name;
  2. 调用父类的方法super.method();
  3. 调用父类的构造方法super(参数);(必须在子类构造方法第一行)

方法重写(Override)

子类重新实现父类的方法:

@Override
public 返回类型 方法名(参数列表) {
    // 重写的实现
}

重写规则
- 方法名、参数列表必须相同
- 返回值类型相同或是子类型(协变返回)
- 访问权限不能更严格
- 不能抛出更宽泛的异常
- 静态方法不能被重写(是隐藏,不是重写)

@Override 注解

  • 标记方法是重写父类方法
  • 编译器会检查是否正确重写
  • 不是必须的,但强烈建议加上

3.4 多态

三个必要条件

  1. 继承(或接口实现)
  2. 方法重写
  3. 父类引用指向子类对象(向上转型)
// 父类引用指向子类对象
Animal animal = new Dog();
animal.makeSound();  // 调用 Dog 的 makeSound(动态绑定)

向上转型与向下转型

// 向上转型(自动):子类 → 父类
Animal animal = new Dog();

// 向下转型(强制):父类 → 子类
Dog dog = (Dog) animal;  // 有风险,可能抛 ClassCastException

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

// Java 16+:模式匹配 instanceof
if (animal instanceof Dog dog) {
    dog.fetch();  // 直接用 dog
}

3.5 抽象类与接口

抽象类

public abstract class Shape {
    // 可以有字段
    protected String color;
    
    // 可以有构造方法
    public Shape(String color) {
        this.color = color;
    }
    
    // 抽象方法:只有声明,没有实现
    public abstract double area();
    
    // 可以有普通方法
    public String getColor() {
        return color;
    }
}
  • 抽象类不能实例化
  • 包含抽象方法的类必须声明为 abstract
  • 子类继承抽象类必须实现所有抽象方法,否则子类也是抽象类

接口

public interface Drawable {
    // 常量(默认 public static final)
    String DEFAULT_COLOR = "black";
    
    // 抽象方法(默认 public abstract)
    void draw();
    
    // 默认方法(Java 8+)
    default void info() {
        System.out.println("可绘制对象");
    }
    
    // 静态方法(Java 8+)
    static Drawable create() {
        return new Circle();
    }
    
    // 私有方法(Java 9+)
    private void helper() {
        // 内部辅助方法
    }
}
  • 接口不能实例化
  • 一个类可以实现多个接口
  • 接口的字段默认是 public static final
  • 接口的方法默认是 public abstract

抽象类 vs 接口

对比项 抽象类 接口
关键字 abstract class interface
继承 单继承 多实现
构造方法 可以有 不能有
字段 各种类型 只能 public static final
方法 抽象 + 具体 抽象 + default + static + private
设计目的 代码复用 + 抽象 定义行为契约

3.6 多态进阶

静态分派与动态分派

// 静态分派(编译时确定):方法重载
public void print(String s) { ... }
public void print(int i) { ... }
// 编译时根据参数的静态类型确定调用哪个方法

// 动态分派(运行时确定):方法重写
Animal a = new Dog();
a.makeSound();
// 运行时根据对象的实际类型确定调用哪个方法

方法分派规则

  • 重载(Overload):编译时,看引用类型 + 参数类型
  • 重写(Override):运行时,看对象的实际类型

3.7 内部类

定义在另一个类内部的类:

public class Outer {
    // 1. 成员内部类
    public class Inner {
        // 可以访问外部类的所有成员(包括 private)
    }
    
    // 2. 静态内部类
    public static class StaticInner {
        // 只能访问外部类的静态成员
    }
    
    void method() {
        // 3. 局部内部类(方法内)
        class LocalInner {
        }
    }
    
    void method2() {
        // 4. 匿名内部类
        Runnable r = new Runnable() {
            @Override
            public void run() {
                System.out.println("匿名内部类");
            }
        };
    }
}

3.8 枚举(Enum)

// 基本枚举
public enum Season {
    SPRING, SUMMER, AUTUMN, WINTER
}

// 带字段和方法的枚举
public enum Season {
    SPRING("春天", "温暖"),
    SUMMER("夏天", "炎热"),
    AUTUMN("秋天", "凉爽"),
    WINTER("冬天", "寒冷");
    
    private final String name;
    private final String desc;
    
    Season(String name, String desc) {
        this.name = name;
        this.desc = desc;
    }
    
    public String getName() { return name; }
    public String getDesc() { return desc; }
}

// 使用
Season s = Season.SPRING;
System.out.println(s.getName());  // 春天

// 常用方法
Season.values();       // 获取所有枚举值
Season.valueOf("SPRING");  // 根据字符串获取枚举值
s.ordinal();           // 获取序号(从 0 开始)

枚举本质上是 final class,继承自 Enum,构造方法默认 private。

关于枚举的深度理解(为什么需要枚举、枚举的本质、枚举实现单例的原理、ordinal 的陷阱),见 枚举类的理解

3.9 记录类(Record,Java 16+)

// 不可变数据类,自动生成构造器、getter、equals、hashCode、toString
public record Point(int x, int y) {
    // 可以添加自定义方法
    public double distance() {
        return Math.sqrt(x * x + y * y);
    }
    
    // 可以添加静态字段/方法
    public static Point origin() {
        return new Point(0, 0);
    }
}

// 使用
Point p = new Point(3, 4);
p.x();          // getter 方法名就是字段名,没有 get 前缀
p.distance();   // 5.0

3.10 密封类(Sealed Class,Java 17+)

// 精确控制谁能继承/实现
public sealed interface Shape permits Circle, Rectangle, Triangle {
}

public final class Circle implements Shape { }
public final class Rectangle implements Shape { }
public final class Triangle implements Shape { }

// 子类也可以是 sealed 或 non-sealed
public sealed class Shape permits Circle, Rectangle { }
public non-sealed class Circle extends Shape { }  // 允许任意继承
public final class Rectangle extends Shape { }

四、集合框架

4.1 集合框架总览

Collection(接口)
├── List(接口)—— 有序、可重复
│   ├── ArrayList        —— 动态数组,查询快,增删慢
│   ├── LinkedList       —— 双向链表,增删快,查询慢
│   ├── Vector           —— 线程安全的动态数组(过时)
│   └── Stack            —— 栈(过时,推荐用 Deque)
│
├── Set(接口)—— 无序、不可重复
│   ├── HashSet          —— 哈希表,最快
│   ├── LinkedHashSet    —— 哈希表 + 链表,保持插入顺序
│   └── TreeSet          —— 红黑树,排序
│
└── Queue(接口)—— 队列
    ├── LinkedList       —— 双端队列
    ├── ArrayDeque       —— 数组双端队列,更快
    ├── PriorityQueue    —— 优先队列(堆)
    └── Deque(接口)—— 双端队列

Map(接口)—— 键值对
├── HashMap              —— 哈希表,最快
├── LinkedHashMap        —— 哈希表 + 链表,保持插入顺序
├── TreeMap              —— 红黑树,排序
├── Hashtable            —— 线程安全(过时)
└── Properties           —— 属性文件

4.2 Collection 接口通用方法

boolean add(E e);                    // 添加元素
boolean remove(Object o);            // 删除元素
boolean contains(Object o);          // 是否包含
int size();                          // 元素个数
boolean isEmpty();                   // 是否为空
void clear();                        // 清空
Iterator<E> iterator();              // 迭代器
Object[] toArray();                  // 转数组
boolean addAll(Collection c);        // 批量添加
boolean removeAll(Collection c);     // 批量删除
boolean retainAll(Collection c);     // 保留交集

4.3 List 接口

ArrayList

  • 底层:动态数组
  • 特点:查询快(O(1)),中间增删慢(O(n))
  • 初始容量:10,扩容 1.5 倍
  • 线程不安全
List<String> list = new ArrayList<>();
list.add("a");        // 添加
list.get(0);          // 获取
list.set(0, "b");     // 修改
list.remove(0);       // 删除
list.size();          // 大小
list.indexOf("a");    // 查找
list.contains("a");   // 包含

LinkedList

  • 底层:双向链表
  • 特点:头尾增删快(O(1)),查询慢(O(n))
  • 实现了 List 和 Deque 接口
LinkedList<String> list = new LinkedList<>();
list.addFirst("a");   // 头部添加
list.addLast("b");    // 尾部添加
list.getFirst();      // 获取头部
list.getLast();       // 获取尾部
list.removeFirst();   // 删除头部
list.removeLast();    // 删除尾部

ArrayList vs LinkedList

对比项 ArrayList LinkedList
底层结构 动态数组 双向链表
随机访问 O(1) 快 O(n) 慢
头尾增删 O(1)(扩容除外) O(1) 快
中间增删 O(n) 慢 O(n)(查找时间)
内存占用 多(每个节点两个指针)
适用场景 频繁查询 频繁头尾增删

大多数场景用 ArrayList 就够了。

4.4 Set 接口

HashSet

  • 底层:HashMap(哈希表)
  • 特点:无序、不可重复、增删查都是 O(1)
  • 线程不安全
Set<String> set = new HashSet<>();
set.add("a");
set.add("a");    // 重复元素不会被添加
set.size();      // 1
set.contains("a");
set.remove("a");

元素的唯一性依赖 hashCode()equals() 方法。

LinkedHashSet

  • 底层:LinkedHashMap(哈希表 + 双向链表)
  • 特点:保持插入顺序,性能略低于 HashSet

TreeSet

  • 底层:TreeMap(红黑树)
  • 特点:排序(自然排序或自定义比较器)
  • 元素必须实现 Comparable 接口或传入 Comparator
Set<Integer> set = new TreeSet<>();
set.add(3);
set.add(1);
set.add(2);
// 遍历顺序:1, 2, 3(自然排序)

// 自定义排序
Set<String> set = new TreeSet<>(Comparator.reverseOrder());

4.5 Map 接口

常用方法

V put(K key, V value);          // 添加/修改
V get(Object key);              // 获取
V remove(Object key);           // 删除
boolean containsKey(Object key); // 是否包含键
boolean containsValue(Object value); // 是否包含值
int size();                     // 大小
boolean isEmpty();              // 是否为空
void clear();                   // 清空

Set<K> keySet();                // 所有键的集合
Collection<V> values();         // 所有值的集合
Set<Map.Entry<K,V>> entrySet(); // 所有键值对的集合

// Java 8+ 新增
V getOrDefault(Object key, V defaultValue);
V putIfAbsent(K key, V value);
boolean remove(Object key, Object value);
boolean replace(K key, V oldValue, V newValue);
V compute(K key, BiFunction remappingFunction);
V merge(K key, V value, BiFunction remappingFunction);

HashMap

  • 底层:数组 + 链表 + 红黑树(Java 8+)
  • 特点:无序、键不可重复、增删查 O(1)
  • 初始容量:16,加载因子:0.75
  • 链表转红黑树:链表长度 > 8 且数组长度 > 64
  • 红黑树转链表:节点数 < 6
  • 线程不安全
Map<String, Integer> map = new HashMap<>();
map.put("a", 1);
map.put("b", 2);
map.get("a");          // 1
map.getOrDefault("c", 0);  // 0

// 遍历方式
// 1. 遍历键
for (String key : map.keySet()) { }

// 2. 遍历值
for (Integer value : map.values()) { }

// 3. 遍历键值对(推荐)
for (Map.Entry<String, Integer> entry : map.entrySet()) {
    String key = entry.getKey();
    Integer value = entry.getValue();
}

// 4. forEach(Java 8+)
map.forEach((k, v) -> System.out.println(k + ": " + v));

LinkedHashMap

  • 底层:HashMap + 双向链表
  • 特点:保持插入顺序(或访问顺序)
  • 可以用来实现 LRU 缓存
// 访问顺序的 LinkedHashMap(LRU 缓存基础)
LinkedHashMap<String, Integer> map = new LinkedHashMap<>(16, 0.75f, true);
// 第三个参数 true = 访问顺序,false = 插入顺序

TreeMap

  • 底层:红黑树
  • 特点:键排序,增删查 O(log n)
  • 键必须实现 Comparable 或传入 Comparator

HashMap vs Hashtable

对比项 HashMap Hashtable
线程安全 不安全 安全(synchronized)
效率
null 键/值 允许一个 null 键,多个 null 值 不允许
初始容量 16 11
推荐 ✅ 推荐 ❌ 过时,用 ConcurrentHashMap 代替

4.6 Queue / Deque

Queue(队列)

Queue<String> queue = new LinkedList<>();
queue.offer("a");    // 入队(添加到尾部)
queue.poll();        // 出队(移除并返回头部)
queue.peek();        // 查看头部(不移除)

Deque(双端队列)

Deque<String> deque = new ArrayDeque<>();
deque.offerFirst("a");   // 头部入队
deque.offerLast("b");    // 尾部入队
deque.pollFirst();       // 头部出队
deque.pollLast();        // 尾部出队
deque.peekFirst();       // 查看头部
deque.peekLast();        // 查看尾部

// 作为栈使用(推荐代替 Stack 类)
deque.push("a");         // 入栈
deque.pop();             // 出栈
deque.peek();            // 查看栈顶

PriorityQueue(优先队列)

// 默认自然排序(最小堆)
PriorityQueue<Integer> pq = new PriorityQueue<>();
pq.offer(3);
pq.offer(1);
pq.offer(2);
pq.poll();   // 1(最小的先出)

// 自定义比较器(最大堆)
PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Comparator.reverseOrder());

4.7 集合工具类:Collections

Collections.sort(list);                    // 排序
Collections.reverse(list);                 // 反转
Collections.shuffle(list);                 // 打乱
Collections.max(list);                     // 最大值
Collections.min(list);                     // 最小值
Collections.swap(list, i, j);              // 交换
Collections.fill(list, value);             // 填充
Collections.copy(dest, src);               // 复制
Collections.binarySearch(list, key);       // 二分查找
Collections.replaceAll(list, oldVal, newVal); // 替换

// 不可变集合
Collections.unmodifiableList(list);
Collections.unmodifiableSet(set);
Collections.unmodifiableMap(map);

// 线程安全集合(不推荐,性能差)
Collections.synchronizedList(list);
Collections.synchronizedMap(map);

4.8 集合选择指南

需求 推荐实现
需要快速随机访问 ArrayList
需要频繁头尾增删 ArrayDeque / LinkedList
需要去重 HashSet
需要去重且排序 TreeSet
需要去重且保持插入顺序 LinkedHashSet
需要键值对 HashMap
需要键值对且排序 TreeMap
需要键值对且保持插入顺序 LinkedHashMap
需要线程安全的 Map ConcurrentHashMap
需要优先队列 PriorityQueue

五、异常处理

5.1 异常体系

Throwable(根类)
├── Error(错误,程序无法处理)
│   ├── OutOfMemoryError
│   ├── StackOverflowError
│   └── NoClassDefFoundError
│
└── Exception(异常,程序可以处理)
    ├── RuntimeException(运行时异常,非受检)
    │   ├── NullPointerException
    │   ├── ArrayIndexOutOfBoundsException
    │   ├── ClassCastException
    │   ├── IllegalArgumentException
    │   └── ArithmeticException
    │
    └── 其他 Exception(受检异常)
        ├── IOException
        ├── SQLException
        ├── ParseException
        └── InterruptedException

5.2 受检异常 vs 非受检异常

对比项 受检异常(Checked) 非受检异常(Unchecked)
父类 Exception(除了 RuntimeException) RuntimeException / Error
编译检查 编译器检查,必须处理 编译器不检查
处理方式 try-catch 或 throws 声明 可以不处理
代表 IOException、SQLException NullPointerException、ArrayIndexOutOfBoundsException
设计哲学 可恢复的异常 编程错误,应该修复代码

5.3 try-catch-finally

try {
    // 可能抛出异常的代码
} catch (ExceptionType1 e) {
    // 处理异常类型1
} catch (ExceptionType2 e) {
    // 处理异常类型2
} finally {
    // 无论是否发生异常都会执行(除非 JVM 退出)
    // 通常用来释放资源
}

注意
- catch 块必须从小到大(先子类后父类)
- finally 不是必须的
- finally 中不要写 return(会覆盖 try/catch 中的 return)
- finally 不执行的情况:System.exit(0)、程序崩溃、线程死亡

5.4 try-with-resources(Java 7+)

自动关闭实现了 AutoCloseable 接口的资源:

// 传统方式(麻烦且容易出错)
InputStream in = null;
try {
    in = new FileInputStream("file.txt");
    // 使用 in
} catch (IOException e) {
    // 处理
} finally {
    if (in != null) {
        try {
            in.close();
        } catch (IOException e) {
            // 关闭异常
        }
    }
}

// try-with-resources(推荐)
try (InputStream in = new FileInputStream("file.txt")) {
    // 使用 in
} catch (IOException e) {
    // 处理
}
// 自动关闭 in,不需要 finally

Java 9+ 可以在 try 中使用已有的变量:try (in) { ... }

5.5 throws 与 throw

throws:声明异常

// 方法声明抛出异常,调用者必须处理
public void readFile(String path) throws IOException, FileNotFoundException {
    // 方法体
}

throw:抛出异常

public void setAge(int age) {
    if (age < 0) {
        throw new IllegalArgumentException("年龄不能为负");
    }
    this.age = age;
}

5.6 自定义异常

// 自定义受检异常
public class BusinessException extends Exception {
    public BusinessException(String message) {
        super(message);
    }
    
    public BusinessException(String message, Throwable cause) {
        super(message, cause);
    }
}

// 自定义运行时异常
public class BusinessRuntimeException extends RuntimeException {
    public BusinessRuntimeException(String message) {
        super(message);
    }
}

5.7 异常处理最佳实践

  1. 具体优于宽泛:捕获具体的异常类型,不要直接 catch Exception
  2. 不要吞异常:catch 块不能空着,至少要打日志
  3. 异常信息要有意义:包含上下文信息,便于排查
  4. 尽量使用非受检异常:受检异常会导致代码混乱
  5. 不要用异常控制流程:异常是处理错误的,不是控制业务逻辑的
  6. ** finally 释放资源**:或者用 try-with-resources
  7. 异常链:包装异常时保留原始异常(new RuntimeException(e)

六、泛型

6.1 什么是泛型

泛型(Generics) 是参数化类型——把类型作为参数传递,让代码可以处理多种类型,同时保持类型安全。

核心好处
- 类型安全:编译时检查类型,不用强转
- 代码复用:一份代码处理多种类型

6.2 泛型类

public class Box<T> {
    private T content;
    
    public void set(T content) {
        this.content = content;
    }
    
    public T get() {
        return content;
    }
}

// 使用
Box<String> stringBox = new Box<>();
stringBox.set("hello");
String s = stringBox.get();  // 不需要强转

Box<Integer> intBox = new Box<>();
intBox.set(123);
Integer i = intBox.get();

6.3 泛型方法

public class Utils {
    // 泛型方法
    public static <T> T getFirst(T[] array) {
        if (array == null || array.length == 0) {
            return null;
        }
        return array[0];
    }
    
    // 多个类型参数
    public static <K, V> void printPair(K key, V value) {
        System.out.println(key + ": " + value);
    }
}

// 使用
String first = Utils.getFirst(new String[]{"a", "b", "c"});

6.4 泛型接口

public interface Comparator<T> {
    int compare(T o1, T o2);
}

public class StringComparator implements Comparator<String> {
    @Override
    public int compare(String o1, String o2) {
        return o1.compareTo(o2);
    }
}

6.5 类型通配符

无界通配符 ?

// 可以接受任意类型的 List
public void printList(List<?> list) {
    for (Object obj : list) {
        System.out.println(obj);
    }
    // list.add("a");  // 编译错误,不能添加(null 除外)
}

上界通配符 ? extends T

// 可以接受 T 及其子类的 List
public double sum(List<? extends Number> list) {
    double sum = 0;
    for (Number n : list) {
        sum += n.doubleValue();
    }
    return sum;
    // list.add(1);  // 编译错误,不能添加(null 除外)
}

PECS 原则:Producer Extends —— 如果是生产者(读取),用 extends。

下界通配符 ? super T

// 可以接受 T 及其父类的 List
public void addNumbers(List<? super Integer> list) {
    list.add(1);
    list.add(2);
    list.add(3);
    // Integer n = list.get(0);  // 编译错误,只能用 Object 接收
}

PECS 原则:Consumer Super —— 如果是消费者(写入),用 super。

6.6 类型擦除

泛型是编译期概念,运行时泛型信息会被擦除:

  • 类型参数替换为上界(默认 Object)
  • 插入类型转换(强转)
  • 插入桥接方法(保持多态)
// 源码
public class Box<T> {
    private T content;
    public T get() { return content; }
}

// 编译后(类型擦除)
public class Box {
    private Object content;
    public Object get() { return content; }
}

// 使用时自动插入强转
Box<String> box = new Box<>();
String s = box.get();  // 编译后:String s = (String) box.get();

类型擦除的影响
- 不能用 new T()(不知道 T 是什么类型)
- 不能用 instanceof T
- 泛型类型不能是基本类型(要用包装类)
- List<String>List<Integer> 运行时是同一个 Class 对象

关于泛型的深度理解(参数化类型的本质、通配符体系与 PECS 原则、类型擦除的历史包袱与七条运行时限制、Java 泛型 vs C++ 模板),见 泛型


七、注解

7.1 什么是注解

注解(Annotation) 是代码中的元数据——给代码添加标记和说明,可以被编译器、工具或运行时读取。

注解不是注释——注释给人看,注解给程序看。

7.2 内置注解

注解 作用
@Override 标记方法是重写父类方法
@Deprecated 标记方法/类已过时,不推荐使用
@SuppressWarnings 抑制编译器警告
@SafeVarargs 抑制泛型可变参数警告(Java 7+)
@FunctionalInterface 标记接口是函数式接口(Java 8+)
@Override
public String toString() {
    return "...";
}

@Deprecated
public void oldMethod() { }

@SuppressWarnings("unchecked")
public void method() {
    List list = new ArrayList();  // 不会有警告
}

7.3 元注解

元注解是用来定义注解的注解:

元注解 作用
@Target 注解可以用在什么地方(类、方法、字段等)
@Retention 注解保留到什么时候(源码/类/运行时)
@Documented 注解会包含在 Javadoc 中
@Inherited 注解可以被子类继承
@Repeatable 注解可以重复使用(Java 8+)

@Target 取值

ElementType.TYPE          // 类、接口、枚举
ElementType.FIELD         // 字段
ElementType.METHOD        // 方法
ElementType.PARAMETER     // 参数
ElementType.CONSTRUCTOR   // 构造方法
ElementType.LOCAL_VARIABLE // 局部变量
ElementType.ANNOTATION_TYPE // 注解
ElementType.PACKAGE       // 包

@Retention 取值

RetentionPolicy.SOURCE    // 只在源码中,编译后丢弃
RetentionPolicy.CLASS     // 保留到 class 文件,运行时不加载(默认)
RetentionPolicy.RUNTIME   // 保留到运行时,可以通过反射读取

7.4 自定义注解

// 定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
    // 注解元素(类似方法)
    String value() default "";
    boolean enabled() default true;
    Level level() default Level.INFO;
    
    enum Level { INFO, DEBUG, WARN, ERROR }
}

// 使用注解
public class UserService {
    @Log(value = "添加用户", level = Log.Level.INFO)
    public void addUser(String name) {
        // ...
    }
}

7.5 注解的读取(反射)

// 读取方法上的注解
Method method = UserService.class.getMethod("addUser", String.class);
Log logAnnotation = method.getAnnotation(Log.class);
if (logAnnotation != null) {
    String value = logAnnotation.value();
    boolean enabled = logAnnotation.enabled();
    // ...
}

关于反射的深度理解(Java 高级特性矩阵、反射解决的根本问题、四大核心能力、五大代价、getXxx vs getDeclaredXxx 的区别),见 反射


八、IO 与 NIO

8.1 IO 流总览

字节流(byte)
├── InputStream(输入)
│   ├── FileInputStream
│   ├── BufferedInputStream
│   ├── DataInputStream
│   └── ObjectInputStream
└── OutputStream(输出)
    ├── FileOutputStream
    ├── BufferedOutputStream
    ├── DataOutputStream
    └── ObjectOutputStream

字符流(char)
├── Reader(输入)
│   ├── FileReader
│   ├── BufferedReader
│   ├── InputStreamReader
│   └── StringReader
└── Writer(输出)
    ├── FileWriter
    ├── BufferedWriter
    ├── OutputStreamWriter
    └── StringWriter

8.2 字节流

FileInputStream / FileOutputStream

// 读取文件
try (FileInputStream fis = new FileInputStream("file.txt")) {
    int data;
    while ((data = fis.read()) != -1) {
        System.out.print((char) data);
    }
} catch (IOException e) {
    e.printStackTrace();
}

// 写入文件
try (FileOutputStream fos = new FileOutputStream("file.txt")) {
    fos.write("Hello".getBytes());
} catch (IOException e) {
    e.printStackTrace();
}

缓冲流

// 带缓冲的字节流,效率更高
try (BufferedInputStream bis = new BufferedInputStream(
        new FileInputStream("file.txt"));
     BufferedOutputStream bos = new BufferedOutputStream(
        new FileOutputStream("output.txt"))) {
    
    byte[] buffer = new byte[1024];
    int len;
    while ((len = bis.read(buffer)) != -1) {
        bos.write(buffer, 0, len);
    }
}

8.3 字符流

FileReader / FileWriter

try (FileReader reader = new FileReader("file.txt");
     FileWriter writer = new FileWriter("output.txt")) {
    
    int c;
    while ((c = reader.read()) != -1) {
        writer.write(c);
    }
}

BufferedReader / BufferedWriter

// 按行读取
try (BufferedReader br = new BufferedReader(new FileReader("file.txt"))) {
    String line;
    while ((line = br.readLine()) != null) {
        System.out.println(line);
    }
}

// 按行写入
try (BufferedWriter bw = new BufferedWriter(new FileWriter("file.txt"))) {
    bw.write("第一行");
    bw.newLine();
    bw.write("第二行");
}

8.4 转换流

// 字节流 → 字符流,可以指定编码
InputStreamReader isr = new InputStreamReader(
    new FileInputStream("file.txt"), StandardCharsets.UTF_8);

OutputStreamWriter osw = new OutputStreamWriter(
    new FileOutputStream("file.txt"), StandardCharsets.UTF_8);

8.5 对象序列化

// 可序列化的类
public class User implements Serializable {
    private static final long serialVersionUID = 1L;
    
    private String name;
    private int age;
    private transient String password;  // transient 字段不序列化
}

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

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

关于序列化的深度理解(为什么需要序列化、三大刚需场景、常用序列化技术对比与选型决策),见 序列化与反序列化

8.6 File 类

File file = new File("path/to/file.txt");

file.exists();           // 是否存在
file.isFile();           // 是否是文件
file.isDirectory();      // 是否是目录
file.getName();          // 文件名
file.getPath();          // 路径
file.getAbsolutePath();  // 绝对路径
file.length();           // 文件大小(字节)
file.lastModified();     // 最后修改时间
file.createNewFile();    // 创建文件
file.mkdir();            // 创建目录
file.mkdirs();           // 创建多级目录
file.delete();           // 删除
file.list();             // 列出子文件/目录名
file.listFiles();        // 列出子文件/目录对象

8.7 NIO(Java 7+,Path / Files)

import java.nio.file.*;

// Path 替代 File
Path path = Paths.get("path/to/file.txt");

// Files 工具类
Files.exists(path);
Files.isRegularFile(path);
Files.isDirectory(path);
Files.size(path);
Files.getLastModifiedTime(path);

// 读写文件
List<String> lines = Files.readAllLines(path, StandardCharsets.UTF_8);
Files.write(path, lines, StandardCharsets.UTF_8);

// 复制、移动、删除
Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
Files.move(source, target, StandardCopyOption.REPLACE_EXISTING);
Files.delete(path);

// 创建目录
Files.createDirectory(path);
Files.createDirectories(path);

// 遍历目录
try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
    for (Path p : stream) {
        System.out.println(p.getFileName());
    }
}

// 递归遍历(Java 8+)
Files.walk(path)
     .filter(Files::isRegularFile)
     .forEach(System.out::println);

九、多线程与并发

9.1 线程基础

进程与线程

  • 进程:程序的一次执行,独立的内存空间
  • 线程:进程内的执行单元,共享进程的内存
  • 一个进程可以有多个线程

创建线程的方式

// 方式1:继承 Thread 类
class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("线程运行中");
    }
}
new MyThread().start();

// 方式2:实现 Runnable 接口(推荐)
class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("线程运行中");
    }
}
new Thread(new MyRunnable()).start();

// 方式3:Lambda(Java 8+)
new Thread(() -> System.out.println("线程运行中")).start();

// 方式4:Callable + Future(有返回值)
Callable<Integer> task = () -> {
    return 42;
};
FutureTask<Integer> future = new FutureTask<>(task);
new Thread(future).start();
Integer result = future.get();  // 阻塞等待结果

线程状态

新建(NEW)→ 就绪(RUNNABLE)→ 运行(RUNNING)→ 终止(TERMINATED)
                       ↓           ↑
                     阻塞(BLOCKED / WAITING / TIMED_WAITING)
状态 说明
NEW 新建,还没调用 start()
RUNNABLE 可运行(就绪 + 运行中)
BLOCKED 阻塞,等待锁
WAITING 无限等待,等待其他线程唤醒
TIMED_WAITING 计时等待,超时自动唤醒
TERMINATED 终止,运行结束

线程常用方法

方法 说明
start() 启动线程
run() 线程执行的代码
sleep(long ms) 休眠(不释放锁)
join() 等待线程结束
yield() 让出 CPU,回到就绪状态
interrupt() 中断线程
isInterrupted() 是否被中断
setPriority(int) 设置优先级(1~10,默认 5)
setDaemon(boolean) 设置为守护线程
currentThread() 获取当前线程对象(静态方法)

9.2 线程安全问题

什么是线程安全

多个线程同时访问共享资源时,不会出现数据不一致的问题。

竞态条件示例

// 线程不安全的计数器
class Counter {
    private int count = 0;
    
    public void increment() {
        count++;  // 不是原子操作:读→加→写
    }
    
    public int getCount() {
        return count;
    }
}

多个线程同时调用 increment(),结果会小于预期。

9.3 synchronized 关键字

同步方法

public synchronized void increment() {
    count++;
}
  • 锁的是当前对象(this)
  • 同一时间只有一个线程能执行这个对象的同步方法

同步代码块

public void increment() {
    synchronized (this) {
        count++;
    }
}
  • 可以更细粒度地控制锁的范围
  • 锁对象可以是任意对象

静态同步方法

public static synchronized void method() {
    // 锁的是类对象(类名.class)
}

9.4 Lock 接口

Lock lock = new ReentrantLock();

lock.lock();          // 加锁
try {
    // 临界区代码
} finally {
    lock.unlock();    // 释放锁(必须在 finally 中)
}

// 尝试获取锁
if (lock.tryLock()) {
    try {
        // 拿到锁了
    } finally {
        lock.unlock();
    }
} else {
    // 没拿到锁,做别的事
}

synchronized vs Lock

对比项 synchronized Lock
实现层面 JVM 层面 API 层面
释放锁 自动释放 必须手动 unlock
锁获取 一直等 可尝试、可超时、可中断
公平锁 不支持 支持
条件变量 不支持(wait/notify) 支持多个 Condition
性能 优化后差不多 差不多

9.5 线程间通信

wait / notify / notifyAll

// 必须在 synchronized 块中调用
synchronized (lock) {
    while (条件不满足) {
        lock.wait();   // 释放锁,等待
    }
    // 条件满足,执行业务
}

// 另一个线程
synchronized (lock) {
    // 修改条件
    lock.notifyAll();  // 唤醒所有等待的线程
}

注意:
- wait() 会释放锁,sleep() 不会释放锁
- 总是在循环中调用 wait()(防止虚假唤醒)
- 优先用 notifyAll() 而不是 notify()

Condition(Lock 的条件变量)

Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();

// 等待
lock.lock();
try {
    while (条件不满足) {
        condition.await();
    }
} finally {
    lock.unlock();
}

// 唤醒
lock.lock();
try {
    condition.signalAll();
} finally {
    lock.unlock();
}

9.6 原子类

java.util.concurrent.atomic 包下的原子操作类,基于 CAS(Compare-And-Swap)实现线程安全。

AtomicInteger count = new AtomicInteger(0);

count.incrementAndGet();    // 原子自增
count.addAndGet(5);         // 原子加 5
count.get();                // 获取值
count.compareAndSet(0, 10); // 比较并设置

常用原子类:
- AtomicIntegerAtomicLongAtomicBoolean
- AtomicReferenceAtomicStampedReference
- AtomicIntegerArrayAtomicLongArray
- LongAdderDoubleAdder(高并发下更好)

9.7 线程池

为什么用线程池

  • 降低资源消耗(复用线程,避免频繁创建销毁)
  • 提高响应速度(任务来了直接执行,不用等线程创建)
  • 提高可管理性(统一管理、监控)

ThreadPoolExecutor

// 核心参数
ThreadPoolExecutor executor = new ThreadPoolExecutor(
    5,           // corePoolSize:核心线程数
    10,          // maximumPoolSize:最大线程数
    60,          // keepAliveTime:空闲线程存活时间
    TimeUnit.SECONDS,  // 时间单位
    new ArrayBlockingQueue<>(100),  // 工作队列
    Executors.defaultThreadFactory(), // 线程工厂
    new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
);

拒绝策略

策略 说明
AbortPolicy 抛出 RejectedExecutionException(默认)
CallerRunsPolicy 由调用线程执行任务
DiscardPolicy 直接丢弃任务
DiscardOldestPolicy 丢弃队列中最老的任务

便捷工厂方法(Executors)

// 固定大小线程池
ExecutorService fixedPool = Executors.newFixedThreadPool(10);

// 单线程线程池
ExecutorService singlePool = Executors.newSingleThreadExecutor();

// 缓存线程池(按需创建,自动回收)
ExecutorService cachedPool = Executors.newCachedThreadPool();

// 定时任务线程池
ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(5);

注意:《阿里巴巴开发规范》不推荐用 Executors 创建线程池,因为默认的队列大小可能导致 OOM。建议直接用 ThreadPoolExecutor。

提交任务

// 无返回值
executor.execute(() -> System.out.println("任务"));

// 有返回值
Future<Integer> future = executor.submit(() -> 42);
Integer result = future.get();  // 阻塞等待结果

// 批量提交
List<Callable<Integer>> tasks = ...;
List<Future<Integer>> futures = executor.invokeAll(tasks);

关闭线程池

executor.shutdown();           // 平缓关闭(等待已提交任务完成)
executor.shutdownNow();        // 立即关闭(尝试中断正在执行的任务)
executor.isShutdown();         // 是否已经关闭
executor.isTerminated();       // 是否所有任务都完成了

9.8 并发集合

集合 说明
ConcurrentHashMap 线程安全的 HashMap,分段锁
CopyOnWriteArrayList 线程安全的 ArrayList,写时复制
CopyOnWriteArraySet 线程安全的 HashSet
ConcurrentSkipListMap 线程安全的 TreeMap
ConcurrentSkipListSet 线程安全的 TreeSet
ConcurrentLinkedQueue 线程安全的非阻塞队列
BlockingQueue 阻塞队列接口
ArrayBlockingQueue 数组实现的有界阻塞队列
LinkedBlockingQueue 链表实现的阻塞队列
PriorityBlockingQueue 优先阻塞队列
SynchronousQueue 同步队列(不存储元素)

9.9 同步工具类

CountDownLatch(倒计时门闩)

// 一个线程等待多个线程完成
CountDownLatch latch = new CountDownLatch(3);

// 工作线程
for (int i = 0; i < 3; i++) {
    new Thread(() -> {
        // 做任务
        latch.countDown();  // 计数减 1
    }).start();
}

latch.await();  // 等待计数变为 0
System.out.println("所有任务完成");

CyclicBarrier(循环栅栏)

// 多个线程互相等待,到达同一起点
CyclicBarrier barrier = new CyclicBarrier(3, () -> {
    System.out.println("所有人都到了,开始下一步");
});

for (int i = 0; i < 3; i++) {
    new Thread(() -> {
        // 做第一阶段任务
        barrier.await();  // 等待其他线程
        // 做第二阶段任务
    }).start();
}

Semaphore(信号量)

// 控制同时访问资源的线程数
Semaphore semaphore = new Semaphore(5);  // 5 个许可

// 工作线程
new Thread(() -> {
    semaphore.acquire();   // 获取许可
    try {
        // 访问资源
    } finally {
        semaphore.release();  // 释放许可
    }
}).start();

9.10 虚拟线程(Virtual Threads,Java 21)

// 创建虚拟线程
Thread.ofVirtual().start(() -> {
    System.out.println("虚拟线程运行中");
});

// 虚拟线程池
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    for (int i = 0; i < 10_000; i++) {
        executor.submit(() -> {
            Thread.sleep(Duration.ofSeconds(1));
            return i;
        });
    }
}

特点:
- 轻量级,由 JVM 管理(不是 OS 线程)
- 可以创建百万级
- 阻塞不浪费资源(自动卸载)
- 适合 IO 密集型任务

关于并发编程的深度理解(CPU 与 IO 的速度差、三大根本问题、JMM 与 happen-before 规则、锁升级机制、AQS 原理、CAS 与 ABA、线程池七大参数、线程安全三策略),见 Java 并发编程


十、函数式编程(Java 8+)

10.1 Lambda 表达式

语法

(参数列表) -> { 方法体 }

简化规则

  • 参数类型可以省略(编译器推断)
  • 单个参数可以省略括号
  • 方法体只有一行可以省略大括号和 return
// 完整形式
(String s) -> { return s.length(); }

// 省略参数类型
(s) -> { return s.length(); }

// 省略括号(单参数)
s -> { return s.length(); }

// 省略大括号和 return(单语句)
s -> s.length()

函数式接口

只有一个抽象方法的接口,Lambda 可以赋值给它:

@FunctionalInterface
public interface Runnable {
    void run();  // 唯一的抽象方法
}

// Lambda 实现 Runnable
Runnable r = () -> System.out.println("Hello");

10.2 内置函数式接口

java.util.function 包下的常用接口:

接口 方法 说明
Supplier<T> T get() 提供者:无参数,返回值
Consumer<T> void accept(T t) 消费者:有参数,无返回值
Function<T, R> R apply(T t) 函数:一个参数,一个返回值
Predicate<T> boolean test(T t) 断言:一个参数,返回 boolean
BiFunction<T, U, R> R apply(T t, U u) 二元函数:两个参数
BiConsumer<T, U> void accept(T t, U u) 二元消费者
BiPredicate<T, U> boolean test(T t, U u) 二元断言
UnaryOperator<T> T apply(T t) 一元运算(输入输出同类型)
BinaryOperator<T> T apply(T t1, T t2) 二元运算(输入输出同类型)
// Supplier
Supplier<String> supplier = () -> "Hello";
String s = supplier.get();

// Consumer
Consumer<String> consumer = s -> System.out.println(s);
consumer.accept("Hello");

// Function
Function<String, Integer> function = s -> s.length();
int len = function.apply("Hello");

// Predicate
Predicate<String> predicate = s -> s.length() > 5;
boolean result = predicate.test("Hello");

10.3 方法引用

Lambda 的简写形式:

类型 语法 示例
静态方法引用 类名::静态方法 Integer::parseInt
实例方法引用 对象::实例方法 str::length
类名引用实例方法 类名::实例方法 String::length
构造方法引用 类名::new ArrayList::new
// Lambda
list.forEach(s -> System.out.println(s));

// 方法引用
list.forEach(System.out::println);

10.4 Stream API

什么是 Stream

Stream 是数据渠道,用来操作集合/数组生成的元素序列。

特点:
- 不存储数据
- 不改变源数据
- 惰性求值(终止操作才执行)

Stream 操作分类

  • 创建操作:创建 Stream
  • 中间操作:处理数据,返回新 Stream(惰性)
  • 终止操作:产生结果,关闭 Stream

创建 Stream

// 从集合
Stream<String> stream1 = list.stream();
Stream<String> stream2 = list.parallelStream();  // 并行流

// 从数组
Stream<String> stream3 = Arrays.stream(array);
Stream<String> stream4 = Stream.of("a", "b", "c");

// 其他
Stream.iterate(0, n -> n + 2);  // 无限流
Stream.generate(Math::random);  // 无限流
IntStream.range(1, 100);        // 范围

中间操作

stream
    .filter(s -> s.length() > 5)    // 过滤
    .map(String::toUpperCase)       // 映射(转换)
    .flatMap(s -> Arrays.stream(s.split("")))  // 扁平化映射
    .distinct()                     // 去重
    .sorted()                       // 排序
    .sorted(Comparator.reverseOrder()) // 自定义排序
    .peek(s -> System.out.println(s))   // 窥视(调试用)
    .limit(10)                      // 限制数量
    .skip(5)                        // 跳过前 N 个
    ;

终止操作

// 遍历
stream.forEach(System.out::println);

// 收集
List<String> list = stream.collect(Collectors.toList());
Set<String> set = stream.collect(Collectors.toSet());
Map<String, Integer> map = stream.collect(Collectors.toMap(
    s -> s, s -> s.length()
));

// 聚合
long count = stream.count();
Optional<String> first = stream.findFirst();
Optional<String> any = stream.findAny();
boolean allMatch = stream.allMatch(s -> s.length() > 5);
boolean anyMatch = stream.anyMatch(s -> s.length() > 5);
boolean noneMatch = stream.noneMatch(s -> s.length() > 5);

// 归约
Optional<Integer> sum = stream.mapToInt(Integer::intValue).reduce(Integer::sum);
Optional<Integer> max = stream.max(Comparator.naturalOrder());
Optional<Integer> min = stream.min(Comparator.naturalOrder());

// 统计(数值流)
IntSummaryStatistics stats = stream.mapToInt(Integer::intValue).summaryStatistics();
stats.getSum();
stats.getAverage();
stats.getMax();
stats.getMin();
stats.getCount();

Collectors 收集器

// 转集合
Collectors.toList()
Collectors.toSet()
Collectors.toCollection(ArrayList::new)

// 转 Map
Collectors.toMap(keyMapper, valueMapper)
Collectors.toMap(keyMapper, valueMapper, mergeFunction)

// 分组
Collectors.groupingBy(分类函数)
Collectors.groupingBy(分类函数, 下游收集器)

// 分区(特殊的分组,按 boolean 分)
Collectors.partitioningBy(断言函数)

// 聚合
Collectors.counting()
Collectors.summingInt(函数)
Collectors.averagingInt(函数)
Collectors.maxBy(比较器)
Collectors.minBy(比较器)

// 连接字符串
Collectors.joining()
Collectors.joining(", ")
Collectors.joining(", ", "[", "]")

// 下游收集器(多级分组)
Collectors.groupingBy(分类函数, Collectors.counting())

分组示例

// 按年龄分组
Map<Integer, List<Person>> byAge = people.stream()
    .collect(Collectors.groupingBy(Person::getAge));

// 按年龄分组,统计每组人数
Map<Integer, Long> countByAge = people.stream()
    .collect(Collectors.groupingBy(
        Person::getAge,
        Collectors.counting()
    ));

// 按年龄分组,每组的名字列表
Map<Integer, List<String>> namesByAge = people.stream()
    .collect(Collectors.groupingBy(
        Person::getAge,
        Collectors.mapping(Person::getName, Collectors.toList())
    ));

10.5 Optional

用来优雅地处理 null,避免 NullPointerException。

创建

Optional<String> opt1 = Optional.of("value");     // 不能是 null
Optional<String> opt2 = Optional.ofNullable(null); // 可以是 null
Optional<String> opt3 = Optional.empty();          // 空的 Optional

常用方法

opt.isPresent();           // 是否有值
opt.isEmpty();             // 是否为空(Java 11+)

opt.get();                 // 获取值(空的话抛异常,不推荐)
opt.orElse("默认值");       // 有值返回值,空的返回默认值
opt.orElseGet(() -> "默认"); // 有值返回值,空的调用 supplier
opt.orElseThrow();         // 空的抛异常(Java 10+)
opt.orElseThrow(() -> new RuntimeException("空"));

opt.ifPresent(s -> System.out.println(s));  // 有值就执行
opt.ifPresentOrElse(                        // 有值/空都处理(Java 9+)
    s -> System.out.println(s),
    () -> System.out.println("空")
);

opt.map(String::length);      // 转换值
opt.flatMap(s -> Optional.of(s.length()));  // 扁平化转换
opt.filter(s -> s.length() > 5);            // 过滤

正确用法

// 不好的用法:还是用 isPresent 判断
if (opt.isPresent()) {
    return opt.get();
} else {
    return "默认值";
}

// 好的用法:函数式风格
return opt.orElse("默认值");

10.6 新日期时间 API(Java 8+)

java.time 包,替代旧的 Date/Calendar。

核心类

说明
LocalDate 日期(年月日)
LocalTime 时间(时分秒)
LocalDateTime 日期时间
ZonedDateTime 带时区的日期时间
Instant 时间戳
Duration 时间段(秒/纳秒)
Period 日期段(年月日)
DateTimeFormatter 格式化/解析

基本使用

// 当前时间
LocalDate today = LocalDate.now();
LocalTime now = LocalTime.now();
LocalDateTime now = LocalDateTime.now();

// 指定时间
LocalDate date = LocalDate.of(2024, 1, 1);
LocalTime time = LocalTime.of(12, 30, 0);
LocalDateTime dt = LocalDateTime.of(2024, 1, 1, 12, 30);

// 获取字段
date.getYear();
date.getMonth();
date.getDayOfMonth();
date.getDayOfWeek();

// 修改(返回新对象,不可变)
date.plusDays(1);
date.minusMonths(1);
date.withYear(2025);

// 比较
date1.isBefore(date2);
date1.isAfter(date2);
date1.isEqual(date2);

// 格式化
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String str = dt.format(formatter);

// 解析
LocalDateTime parsed = LocalDateTime.parse("2024-01-01 12:30:00", formatter);

// 时间段
Duration duration = Duration.between(start, end);
duration.toHours();
duration.toMinutes();

Period period = Period.between(startDate, endDate);
period.getYears();
period.getMonths();
period.getDays();

十一、Java 新特性演进

11.1 Java 8(2014)—— 函数式革命

特性 说明
Lambda 表达式 函数式编程的基础
Stream API 集合的函数式操作
Optional 优雅处理 null
新日期时间 API java.time 包
接口默认方法/静态方法 接口可以有实现
重复注解 同一注解可以用多次
方法引用 Lambda 的简写
CompletableFuture 异步编程

11.2 Java 9(2017)—— 模块化

特性 说明
JPMS 模块系统 Project Jigsaw,模块化 JDK
JShell 交互式 REPL
集合工厂方法 List.of()Set.of()Map.of()
接口私有方法 接口中可以有 private 方法
Stream 增强 takeWhiledropWhileofNullable
Optional 增强 stream()ifPresentOrElse()or()
HTTP Client(孵化器) 新的 HTTP 客户端
VarHandle 变量句柄,替代 Unsafe

11.3 Java 11(2018)—— LTS 过渡版

特性 说明
var 局部变量类型推断 减少样板代码
Lambda 中使用 var (var x, var y) -> x + y
HTTP Client 正式版 支持 HTTP/2、异步
String API 增强 isBlank()strip()lines()repeat()
Files API 增强 readString()writeString()
ZGC(实验) 低延迟垃圾回收器
Epsilon GC 无操作 GC(性能测试用)
飞行记录器(JFR) 性能分析工具

11.4 Java 17(2021)—— 数据导向编程

特性 说明
Record 类 不可变数据类,自动生成方法
Sealed Class 密封类,精确控制继承
Pattern Matching for switch(预览) switch 模式匹配
Pattern Matching for instanceof instanceof 模式匹配
Text Block 多行字符串 """..."""
Helpful NullPointerException 精确的 NPE 信息
ZGC 正式版 亚毫秒级 GC
Shenandoah GC 低延迟 GC
移除 Nashorn 移除 JavaScript 引擎

11.5 Java 21(2023)—— 并发革命

特性 说明
虚拟线程 Project Loom,轻量级线程
结构化并发(预览) 并发编程的结构化
模式匹配正式版 switch 模式匹配正式化
Record Pattern 记录模式,解构 record
Pattern Matching for switch switch 模式匹配 + 守卫条件
Sequenced Collections 有序集合接口
String Templates(预览) 字符串模板
Foreign Function & Memory API(预览) 替代 JNI
Generational ZGC 分代 ZGC
作用域值(预览) 替代 ThreadLocal

11.6 演进趋势总结

Java 8: 函数式编程入门(Lambda + Stream)
   ↓
Java 9~11: 基础设施完善(模块 + var + HTTP Client)
   ↓
Java 17: 数据导向编程(Record + Sealed + Pattern Matching)
   ↓
Java 21: 并发范式改变(虚拟线程 + 结构化并发)
   ↓
未来: 值类型(Valhalla)+ 更完善的模式匹配

十二、JVM 基础

12.1 JVM 内存结构

JVM 内存
├── 线程私有
│   ├── 程序计数器(PC Register)—— 当前执行的字节码指令地址
│   ├── 虚拟机栈(VM Stack)—— 栈帧,局部变量表、操作数栈
│   └── 本地方法栈(Native Method Stack)—— 本地方法的栈
│
└── 线程共享
    ├── 堆(Heap)—— 对象实例、数组(GC 的主要区域)
    │   ├── 新生代(Young)
    │   │   ├── Eden
    │   │   ├── Survivor 0
    │   │   └── Survivor 1
    │   └── 老年代(Old)
    │
    └── 方法区(Method Area)—— 类信息、常量、静态变量
        └── 运行时常量池

12.2 类加载机制

类加载过程

加载 → 验证 → 准备 → 解析 → 初始化 → 使用 → 卸载
阶段 说明
加载 通过类名获取字节码,转换为方法区的数据结构,生成 Class 对象
验证 验证字节码的正确性(文件格式、元数据、字节码、符号引用)
准备 为静态变量分配内存,设置默认值
解析 符号引用替换为直接引用
初始化 执行静态代码块和静态变量赋值

类加载器

加载器 说明
Bootstrap ClassLoader 启动类加载器,加载核心类库(JAVA_HOME/jre/lib)
Extension ClassLoader 扩展类加载器,加载扩展类库(jre/lib/ext)
Application ClassLoader 应用程序类加载器,加载用户类路径的类
Custom ClassLoader 自定义类加载器

双亲委派模型

Application ClassLoader
        ↓ 委派
Extension ClassLoader
        ↓ 委派
Bootstrap ClassLoader
  • 类加载时先让父类加载器尝试加载
  • 父类加载不了才自己加载
  • 好处:安全(核心类不会被篡改)、避免重复加载

12.3 垃圾回收(GC)

什么是垃圾

不再被引用的对象。

如何判断垃圾

算法 说明 问题
引用计数法 每个对象维护引用计数,计数为 0 就是垃圾 循环引用问题
可达性分析 从 GC Roots 出发,不可达的就是垃圾 ——

GC Roots
- 虚拟机栈中引用的对象
- 方法区中静态变量引用的对象
- 方法区中常量引用的对象
- 本地方法栈中引用的对象

垃圾回收算法

算法 说明 优点 缺点
标记-清除 标记垃圾,然后清除 简单 内存碎片
复制算法 内存分两块,复制存活对象到另一块 无碎片 浪费空间
标记-整理 标记存活对象,移动到一端 无碎片 移动开销大
分代收集 不同代用不同算法 综合各算法优点 ——

分代收集

  • 新生代:对象存活率低 → 复制算法(Eden + 两个 Survivor,8:1:1)
  • 老年代:对象存活率高 → 标记-整理 或 标记-清除

垃圾收集器

收集器 算法 特点
Serial 新生代 复制 单线程,简单
Serial Old 老年代 标记-整理 单线程
Parallel Scavenge 新生代 复制 多线程,吞吐量优先
Parallel Old 老年代 标记-整理 多线程
CMS 老年代 标记-清除 低延迟,并发收集
G1 全代 标记-整理 + 复制 区域化,可预测停顿
ZGC 全代 —— 极低延迟(亚毫秒级)
Shenandoah 全代 —— 极低延迟

常用 JVM 参数

# 堆内存
-Xms512m          # 初始堆大小
-Xmx1024m         # 最大堆大小
-Xmn256m          # 新生代大小

# 元空间(方法区)
-XX:MetaspaceSize=128m
-XX:MaxMetaspaceSize=256m

# 垃圾收集器
-XX:+UseG1GC
-XX:+UseZGC

# 日志
-XX:+PrintGCDetails
-Xlog:gc*:file=gc.log

# 其他
-XX:SurvivorRatio=8      # Eden:Survivor = 8:1
-XX:MaxTenuringThreshold=15  # 晋升老年代的年龄

关于类加载与执行引擎的深度理解(类加载五阶段、双亲委派模型与打破场景、类实例化五步与对象内存布局、JIT 混合模式与分层编译、逃逸分析与方法内联、AOT vs JIT),见 类加载机制与执行引擎


十三、常用工具类

13.1 Objects

Objects.equals(a, b);           // 安全比较(null 安全)
Objects.deepEquals(a, b);       // 深度比较(数组)
Objects.hashCode(o);            // 哈希码
Objects.toString(o);            // 转字符串
Objects.toString(o, "默认值");   // null 时返回默认值
Objects.requireNonNull(o);      // 检查非 null
Objects.requireNonNull(o, "不能为空");
Objects.isNull(o);              // 是否为 null
Objects.nonNull(o);             // 是否非 null

13.2 Math

Math.abs(-5);          // 绝对值:5
Math.max(3, 5);        // 最大值:5
Math.min(3, 5);        // 最小值:3
Math.pow(2, 3);        // 幂:8.0
Math.sqrt(16);         // 平方根:4.0
Math.cbrt(27);         // 立方根:3.0
Math.ceil(3.2);        // 向上取整:4.0
Math.floor(3.8);       // 向下取整:3.0
Math.round(3.5);       // 四舍五入:4
Math.random();         // 随机数 [0, 1)
Math.PI;               // 圆周率
Math.E;                // 自然常数

13.3 String

// 基本
str.length();
str.isEmpty();
str.isBlank();              // Java 11+,空白也算空
str.charAt(0);

// 查找
str.indexOf("a");
str.lastIndexOf("a");
str.contains("a");
str.startsWith("a");
str.endsWith("a");

// 截取
str.substring(1);
str.substring(1, 3);

// 转换
str.toUpperCase();
str.toLowerCase();
str.trim();                 // 去除首尾空格
str.strip();                // Java 11+,去除首尾空白(含 Unicode)
str.replace("a", "b");
str.replaceAll("regex", "b");
str.replaceFirst("regex", "b");

// 分割与连接
str.split(",");
String.join(",", list);     // 静态方法

// 其他
str.toCharArray();
str.getBytes();
str.format("Hello %s", "World");
str.compareTo("other");
str.equals("other");
str.equalsIgnoreCase("OTHER");

// Java 11+
str.lines();                // 按行转 Stream
str.repeat(3);              // 重复 3 次
str.stripLeading();
str.stripTrailing();

13.4 System

System.currentTimeMillis();    // 当前时间戳(毫秒)
System.nanoTime();             // 纳秒(用于计时)
System.getProperty("java.version");  // 系统属性
System.getenv("PATH");         // 环境变量
System.out.println();          // 标准输出
System.err.println();          // 错误输出
System.arraycopy(src, srcPos, dest, destPos, length); // 数组复制
System.gc();                   // 建议 GC(不保证执行)
System.exit(0);                // 退出 JVM

附录

A. 常用命令

# 编译
javac HelloWorld.java

# 运行
java HelloWorld
java -jar app.jar

# 查看字节码
javap -c HelloWorld

# 查看 JVM 参数
java -XX:+PrintFlagsFinal -version

# 查看进程
jps
jps -l

# 查看堆信息
jmap -heap <pid>
jmap -dump:format=b,file=heap.hprof <pid>

# 查看栈信息
jstack <pid>

# 性能分析
jconsole
jvisualvm

B. 参考资源