Java 核心知识点
学习哲学:以"面向对象、集合框架、并发编程、JVM原理"为骨架,以 Oracle 官方 Java Tutorials 为锚点,以 IDEA 调试和 JVM 监控为手术刀,以真实企业级后端开发为验收标准。
版本说明:本教程以 Java SE 8 (LTS) 为基线,标注 Java 8/9/11/14/17/21 等关键版本的新特性。标记
[JDK x+]表示自该版本起可用。
第一阶段:Java 概述与开发环境
1.1 Java 是什么
Java 是 Sun Microsystems(后被 Oracle 收购)于 1995 年发布的面向对象编程语言,核心理念是 "Write Once, Run Anywhere"(一次编写,到处运行)。Java 程序编译为字节码后在 JVM(Java Virtual Machine) 上运行,屏蔽了底层操作系统差异。
Java 三大平台:
| 平台 | 全称 | 定位 |
|---|---|---|
| Java SE | Standard Edition | 核心基础,包含语言规范、核心 API、JVM |
| Java EE(Jakarta EE) | Enterprise Edition | 企业级 Web/分布式应用,构建在 SE 之上 |
| Java ME | Micro Edition | 嵌入式与移动设备 |
JDK 关键 LTS 版本:
| 版本 | 发布时间 | 核心新特性 |
|---|---|---|
| Java 8 (LTS) | 2014.03 | Lambda、Stream API、Optional、新日期时间 API |
| Java 11 (LTS) | 2018.09 | HTTP Client、var 局部变量推断(10)、模块化(9) |
| Java 17 (LTS) | 2021.09 | Record、Sealed Class、Pattern Matching for switch(预览) |
| Java 21 (LTS) | 2023.09 | Virtual Thread、Record Pattern、Pattern Matching for switch(正式) |
1.2 JVM / JRE / JDK 关系
职责划分:
| 组件 | 职责 |
|---|---|
| JDK | 开发工具包,包含 JRE + 编译器(javac)、打包工具(jar)、文档工具(javadoc)等 |
| JRE | 运行环境,包含 JVM + 核心类库,仅能运行 Java 程序不能编译 |
| JVM | 虚拟机,执行字节码,负责内存管理、垃圾回收、线程调度、安全检查 |
从 Java 11 开始,Oracle 不再单独发布 JRE,JDK 本身即包含运行环境。
1.3 Java 程序编译与运行
编译命令:javac Hello.java → 生成 Hello.class运行命令:java Hello → JVM 执行字节码
1.4 本周实践任务
- [ ] 安装 JDK 17 或 21,配置
JAVA_HOME和PATH环境变量 - [ ] 用
javac和java命令手工编译运行一个 HelloWorld - [ ] 用
javap -c Hello反编译 class 文件,观察字节码指令 - [ ] 运行
jshell(Java 9+ 交互式编程环境)体验快速代码验证 - [ ] 对比
java -version输出的不同 JDK 版本信息
第二阶段:语言基础
2.1 数据类型
Java 是强类型语言,每个变量必须先声明类型。数据类型分为基本类型(Primitive Type)和引用类型(Reference Type)。
8 种基本类型:
| 类型 | 大小 | 默认值 | 范围 | 包装类 |
|---|---|---|---|---|
| byte | 1 字节 | 0 | -128 ~ 127 | Byte |
| short | 2 字节 | 0 | -32768 ~ 32767 | Short |
| int | 4 字节 | 0 | -2³¹ ~ 2³¹-1(约 ±21 亿) | Integer |
| long | 8 字节 | 0L | -2⁶³ ~ 2⁶³-1 | Long |
| float | 4 字节 | 0.0f | ±3.4E-38 ~ ±3.4E+38(6-7 位有效数字) | Float |
| double | 8 字节 | 0.0d | ±1.7E-308 ~ ±1.7E+308(15 位有效数字) | Double |
| char | 2 字节 | '\u0000' | 0 ~ 65535(Unicode 字符) | Character |
| boolean | JVM 依赖 | false | true / false | Boolean |
boolean大小未精确定义,JVM 中通常用 int 表示,数组中以 byte 存储。
引用类型:类(Class)、接口(Interface)、数组(Array)、枚举(Enum)、注解(Annotation)、记录(Record [JDK 14+])。
基本类型 vs 包装类:
| 特性 | 基本类型 | 包装类 |
|---|---|---|
| 存储位置 | 栈(局部变量)/ 堆(成员变量) | 堆 |
| 默认值 | 数值型 0,boolean false | null |
| 泛型支持 | ❌ 不支持 | ✅ 支持 |
| 性能 | 高 | 较低(装箱开销) |
| 方法调用 | 无 | 有工具方法 |
int a = 100; // 基本类型
Integer b = Integer.valueOf(100); // 包装类(推荐,复用缓存 -128~127)
Integer c = 100; // 自动装箱(Auto-boxing,编译后等价于 Integer.valueOf(100))
int d = c; // 自动拆箱(Unboxing)
2.2 变量
Java 变量分三类:
| 变量类型 | 声明位置 | 生命周期 | 默认值 |
|---|---|---|---|
| 局部变量 | 方法/代码块内 | 方法执行期间 | ❌ 无默认值,必须显式初始化 |
| 成员变量(实例变量) | 类中,方法外,无 static | 与对象共存亡 | ✅ 有默认值(0 / false / null) |
| 静态变量(类变量) | 类中,有 static | 类加载到卸载 | ✅ 有默认值 |
局部变量类型推断 [JDK 10+]:
var list = new ArrayList<String>(); // 编译器推断为 ArrayList<String>
var name = "Hello"; // 推断为 String
// var x; ❌ 编译错误,必须初始化
var仅用于局部变量,不能用于成员变量、方法参数、返回类型。
2.3 数组
// 声明与初始化
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[][] jagged = {{1, 2}, {3, 4, 5}}; // 锯齿数组
// 数组工具类
Arrays.sort(arr1); // 排序
Arrays.binarySearch(arr1, 3); // 二分查找
Arrays.copyOf(arr1, 10); // 扩容
Arrays.toString(arr1); // 打印
数组长度
length是属性而非方法。数组一旦创建,长度不可变。
2.4 运算符
| 类别 | 运算符 | 说明 |
|---|---|---|
| 算术 | + - * / % | 加减乘除取模 |
| 赋值 | = += -= *= /= %= | 赋值及复合赋值 |
| 自增自减 | ++ -- | 前缀(先运算后用)vs 后缀(先用后运算) |
| 关系 | == != > < >= <= | 比较运算,结果为 boolean |
| 逻辑 | && || ! | 短路与、短路或、非 |
| 位运算 | & | ^ ~ << >> >>> | 按位与/或/异或/非,左移,算数右移,逻辑右移 |
| 三元 | ? : | 条件 ? 值1 : 值2 |
== vs equals():
String s1 = new String("hello");
String s2 = new String("hello");
System.out.println(s1 == s2); // false,比较引用地址
System.out.println(s1.equals(s2)); // true,比较内容
==比较基本类型的值或引用类型的地址;equals()比较对象内容(需重写)。
2.5 流程控制
// if-else
if (condition) { ... } else if (condition2) { ... } else { ... }
// switch(支持 byte/short/int/char/String/枚举)
switch (value) {
case 1: ... break;
case 2: ... break;
default: ...
}
Switch 表达式 [JDK 14+]:
String result = switch (day) {
case MONDAY, FRIDAY -> "工作日";
case SATURDAY, SUNDAY -> "周末";
default -> "未知";
};
循环:
while (condition) { ... }
do { ... } while (condition);
for (int i = 0; i < 10; i++) { ... }
for (Type item : collection) { ... } // 增强 for,适用于数组和 Iterable
分支:break(跳出循环)、continue(跳过本次迭代)、return(返回方法)、label(带标签的 break/continue)。
2.6 本周实践任务
- [ ] 验证基本类型的默认值和取值范围
- [ ] 用
javap -c观察Integer.valueOf(100)是否走缓存 - [ ] 对比
String s = "hello"与new String("hello")的==和equals结果 - [ ] 用
var声明局部变量,观察 IDE 的类型推断提示 - [ ] 用 Switch 表达式替代传统 switch-case,比较代码简洁度
第三阶段:面向对象编程(OOP)
3.1 OOP 四大基本特征
| 特征 | 含义 | Java 实现 |
|---|---|---|
| 封装(Encapsulation) | 隐藏内部实现,仅暴露接口 | private + getter/setter |
| 继承(Inheritance) | 子类复用父类属性和方法 | extends(单继承) |
| 多态(Polymorphism) | 同一行为的不同表现形式 | 方法重写 + 父类引用指向子类对象 |
| 抽象(Abstraction) | 提取共性忽略细节 | abstract 类 / interface |
3.2 类与对象
public class Person {
// 成员变量(字段)
private String name;
private int age;
// 静态变量
public static final String SPECIES = "Homo sapiens";
// 构造方法
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// 实例方法
public String getName() { return name; }
// 静态方法
public static void printSpecies() {
System.out.println(SPECIES);
}
// 重写 Object 方法
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + "}";
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Person)) return false;
Person person = (Person) o;
return age == person.age && Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
this 关键字的两种用法:① 引用当前对象实例;② 在构造方法中调用本类的其他构造方法 this(...)。
3.3 封装与访问控制
| 修饰符 | 同类 | 同包 | 子类 | 任何位置 |
|---|---|---|---|---|
| private | ✅ | ❌ | ❌ | ❌ |
| default(无修饰符) | ✅ | ✅ | ❌ | ❌ |
| protected | ✅ | ✅ | ✅ | ❌ |
| public | ✅ | ✅ | ✅ | ✅ |
JavaBean 规范:① 无参构造方法;② 属性私有化 private;③ 提供 getter/setter 方法;④ 实现 Serializable(可选)。
3.4 继承
Java 支持单继承(一个类只能有一个直接父类),所有类的最终祖先都是 java.lang.Object。
public class Student extends Person {
private String school;
public Student(String name, int age, String school) {
super(name, age); // 调用父类构造方法
this.school = school;
}
@Override
public String toString() {
return "Student{" + super.toString() + ", school='" + school + "'}";
}
}
方法重写(Override)vs 方法重载(Overload):
| 维度 | Override(重写) | Overload(重载) |
|---|---|---|
| 发生位置 | 父子类之间 | 同一个类中 |
| 方法签名 | 完全相同 | 方法名相同,参数列表不同 |
| 返回类型 | 相同或协变(子类型) | 可以不同 |
| 访问修饰符 | 不能更严格 | 无限制 |
| 运行时 | 动态绑定(多态) | 静态绑定(编译期决定) |
@Override注解让编译器检查是否真的是重写,强烈推荐使用。
super 关键字:① 调用父类构造方法 super(args)(必须放在子类构造方法首行);② 访问父类成员 super.method() / super.field。
3.5 多态
// 编译时类型 = Person,运行时类型可能是 Student
Person p = new Student("Alice", 20, "PKU");
p.getName(); // 调用 Person.getName() —— 不涉及多态
p.toString(); // 调用 Student.toString() —— 运行时多态
// 向下转型(需要确认类型)
if (p instanceof Student) {
Student s = (Student) p;
s.getSchool();
}
// Pattern Matching for instanceof [JDK 16+]
if (p instanceof Student s) {
System.out.println(s.getSchool());
}
instanceof 模式匹配 [JDK 16+]:if (obj instanceof String s && s.length() > 5),一步完成类型判断和变量绑定。
3.6 抽象类与接口
| 维度 | 抽象类(abstract class) | 接口(interface) |
|---|---|---|
| 关键字 | abstract class | interface |
| 构造方法 | ✅ 有 | ❌ 无 |
| 成员变量 | 无限制 | 只能 public static final 常量 |
| 方法 | 抽象方法 + 具体方法 | 抽象方法 + default 方法 [JDK 8+] + static 方法 [JDK 8+] + private 方法 [JDK 9+] |
| 继承 | 单继承 extends | 多实现 implements |
| 设计意图 | "is-a" 关系,模板模式 | "can-do" 能力,行为契约 |
// 接口
public interface Flyable {
void fly(); // 抽象方法
default void land() { // 默认方法 [JDK 8+]
System.out.println("准备着陆...");
}
static String getType() { // 静态方法 [JDK 8+]
return "飞行物";
}
private void checkWeather() { // 私有方法 [JDK 9+]
// 内部复用逻辑
}
}
函数式接口(Functional Interface) [JDK 8+]:只有一个抽象方法的接口。@FunctionalInterface 注解标记。常见的有 Runnable、Comparator、Callable、Predicate、Function、Consumer、Supplier。
3.7 内部类
| 类型 | 定义位置 | 特点 |
|---|---|---|
| 成员内部类 | 类中方法外 | 可访问外部类所有成员,Outer.this.field |
| 静态内部类 | 类中方法外,static 修饰 | 只能访问外部类静态成员,常配合 Builder 模式 |
| 局部内部类 | 方法体内 | 作用域仅限于方法内,可访问方法内 final / effectively final 变量 |
| 匿名内部类 | 表达式(new 接口/抽象类) | 一次性使用,常用于事件监听。Lambda 表达式可简化 [JDK 8+] |
// 匿名内部类 → Lambda 表达式简化
button.addActionListener(new ActionListener() {
@Override public void actionPerformed(ActionEvent e) {
System.out.println("clicked");
}
});
// Lambda 等价写法 [JDK 8+]
button.addActionListener(e -> System.out.println("clicked"));
3.8 枚举(Enum)
[JDK 5+]
public enum Day {
MONDAY("周一"), TUESDAY("周二"), WEDNESDAY("周三");
private final String chineseName;
Day(String name) { this.chineseName = name; } // 枚举构造方法默认 private
public String getChineseName() { return chineseName; }
}
Day today = Day.MONDAY;
System.out.println(today.name()); // "MONDAY"
System.out.println(today.ordinal()); // 0
System.out.println(today.getChineseName()); // "周一"
Day d = Day.valueOf("MONDAY"); // 字符串转枚举
所有枚举默认继承
java.lang.Enum,无法再继承其他类,但可实现接口。枚举天然是线程安全的单例。
3.9 记录类(Record)
[JDK 14 预览,JDK 16 正式]
// 自动生成:构造方法、equals()、hashCode()、toString()、getter(无 get 前缀)
public record Point(int x, int y) {
// 紧凑构造方法(校验)
public Point {
if (x < 0 || y < 0) throw new IllegalArgumentException();
}
// 可添加实例方法
public int distance() { return x + y; }
}
Point p = new Point(3, 4);
System.out.println(p.x()); // 3(访问器,方法名 = 字段名)
System.out.println(p); // Point[x=3, y=4]
Record 是不可变数据载体,所有字段自动
private final,不能继承任何类(默认继承java.lang.Record)。
3.10 本周实践任务
- [ ] 设计一个继承体系(Animal → Dog/Cat),用多态统一调用
makeSound() - [ ] 对比
abstract class和interface的设计选择场景 - [ ] 用
record替代传统 POJO,对比代码量 - [ ] 实现一个函数式接口并用 Lambda 表达式传递行为
- [ ] 验证
equals()与hashCode()的契约:相等的对象必须有相等的 hashCode
第四阶段:异常处理
4.1 异常体系
| 类别 | 特点 | 示例 |
|---|---|---|
| Error | JVM 级严重错误,不应捕获 | OutOfMemoryError、StackOverflowError |
| 受检异常(Checked) | 编译期强制 try-catch 或 throws | IOException、SQLException、ClassNotFoundException |
| 运行时异常(Unchecked) | 编译期不检查,通常为编程错误 | NullPointerException、ArrayIndexOutOfBoundsException、IllegalArgumentException |
4.2 异常处理语法
try {
// 可能抛出异常的代码
int result = 10 / 0;
} catch (ArithmeticException e) {
System.err.println("算术异常: " + e.getMessage());
} catch (Exception e) {
System.err.println("其他异常: " + e);
} finally {
// 无论是否异常都会执行(资源释放)
System.out.println("finally 块执行");
}
// try-with-resources [JDK 7+]:自动关闭资源
try (FileInputStream fis = new FileInputStream("file.txt");
BufferedReader br = new BufferedReader(new InputStreamReader(fis))) {
String line = br.readLine();
} catch (IOException e) {
e.printStackTrace();
}
// 无需手动 close(),实现了 AutoCloseable 接口的资源会自动关闭
finally的执行时机:除System.exit(0)或 JVM 崩溃外,finally总会执行。即使 try/catch 中有return,finally也会在 return 之前执行。
异常处理原则:① 早抛出晚捕获;② 不要吞掉异常(空 catch 块);③ 抛出具体异常而不是泛化 Exception;④ finally 或 try-with-resources 确保资源释放。
4.3 本周实践任务
- [ ] 验证
finally在return之前的执行顺序 - [ ] 用 try-with-resources 打开文件读写,对比传统的 try-catch-finally
- [ ] 自定义异常类,区分业务异常
BusinessException和系统异常SystemException - [ ] 理解异常链(cause),用
throw new RuntimeException("包装", originalException)保留根因
第五阶段:常用核心类
5.1 String 与 StringBuilder
| 类 | 可变性 | 线程安全 | 性能 | 适用场景 |
|---|---|---|---|---|
| String | 不可变(final 类) | ✅ final 保证 | 低(拼接时频繁创建对象) | 少量拼接、作为 Map key |
| StringBuilder | 可变 | ❌ 非线程安全 | 高 | 单线程大量字符串拼接 |
| StringBuffer | 可变 | ✅ 线程安全(synchronized) | 中 | 多线程字符串拼接 |
// String 不可变 —— 每次拼接都产生新对象
String s = "Hello";
s = s + " World"; // 创建新 String 对象
// String 常量池(字符串池)
String a = "abc"; // 字面量,指向常量池
String b = "abc"; // 指向同一常量池对象
String c = new String("abc"); // 强制在堆上创建新对象
System.out.println(a == b); // true
System.out.println(a == c); // false
// intern() 将堆上 String 放入常量池
System.out.println(a == c.intern()); // true
// StringBuilder 高效拼接
StringBuilder sb = new StringBuilder();
sb.append("Hello").append(" ").append("World");
String result = sb.toString();
// 文本块(Text Block)[JDK 13 预览,15 正式]
String json = """
{
"name": "Alice",
"age": 20
}
""";
5.2 Object 类核心方法
所有 Java 类的根类是 java.lang.Object,核心方法:
| 方法 | 说明 |
|---|---|
toString() | 返回对象字符串表示,默认返回 类名@哈希码 |
equals(Object) | 默认比较引用地址(==),需重写实现内容比较 |
hashCode() | 返回哈希码。重写 equals() 必须重写 hashCode() |
clone() | 浅克隆。需实现 Cloneable 接口 |
getClass() | 返回运行时 Class 对象 |
finalize() | 垃圾回收前调用(JDK 9 起已废弃,不建议使用) |
wait() / notify() / notifyAll() | 多线程同步(见并发章节) |
5.3 包装类与 Math
// 包装类常量与方法
Integer.MAX_VALUE; // 2147483647
Integer.parseInt("123"); // String → int
Integer.valueOf(100); // int → Integer(使用缓存 -128 ~ 127)
// Math 工具
Math.abs(-5); // 5
Math.max(3, 7); // 7
Math.pow(2, 10); // 1024.0
Math.sqrt(16); // 4.0
Math.random(); // [0.0, 1.0) 随机数
Math.round(3.5); // 4(四舍五入)
Math.floor(3.9); // 3.0
Math.ceil(3.1); // 4.0
5.4 新日期时间 API
[JDK 8+](java.time 包),替代旧的 java.util.Date 和 java.util.Calendar。
| 类 | 说明 |
|---|---|
LocalDate | 日期(年-月-日) |
LocalTime | 时间(时:分:秒) |
LocalDateTime | 日期 + 时间 |
ZonedDateTime | 带时区的日期时间 |
Instant | 时间戳(自 1970-01-01T00:00:00Z) |
Duration | 时间间隔(时分秒) |
Period | 日期间隔(年月日) |
DateTimeFormatter | 格式化器(线程安全,替代 SimpleDateFormat) |
LocalDate today = LocalDate.now();
LocalDate birthday = LocalDate.of(2000, 1, 15);
Period age = Period.between(birthday, today);
long days = ChronoUnit.DAYS.between(birthday, today);
String formatted = LocalDateTime.now()
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
5.5 本周实践任务
- [ ] 用
StringBuilder拼接 10 万条字符串,对比+操作的时间差异 - [ ] 理解 String 常量池:用
intern()验证字符串池行为 - [ ] 重写
equals()和hashCode(),测试 HashMap 存储时的一致性 - [ ] 使用
LocalDate+Period计算两个日期之间隔了多少个闰年 - [ ] 用文本块(Text Block)编写 SQL 和 JSON 字符串,对比传统写法
第六阶段:泛型
[JDK 5+]
6.1 为什么使用泛型
泛型提供了编译期类型安全检查,避免 ClassCastException,同时消除强制类型转换。
// JDK 5 之前
List list = new ArrayList();
list.add("hello");
list.add(123);
String s = (String) list.get(0); // 需要强制转换,容易出错
// JDK 5+ 泛型
List<String> list = new ArrayList<>();
list.add("hello");
// list.add(123); ❌ 编译错误
String s = list.get(0); // 无需强制转换
6.2 泛型类、接口、方法
// 泛型类
public class Box<T> {
private T content;
public T get() { return content; }
public void set(T content) { this.content = content; }
}
// 泛型接口
public interface Repository<T, ID> {
T findById(ID id);
void save(T entity);
}
// 泛型方法
public static <T> T getFirst(List<T> list) {
return list.isEmpty() ? null : list.get(0);
}
6.3 类型通配符与边界
| 语法 | 含义 | 适用场景 |
|---|---|---|
<?> | 无限定通配符 | 仅读取,不依赖具体类型 |
<? extends T> | 上界通配符(T 及其子类) | 生产者(读取) —— PECS: Producer Extends |
<? super T> | 下界通配符(T 及其父类) | 消费者(写入) —— PECS: Consumer Super |
<T extends Comparable<T>> | 类型参数上界约束 | 要求 T 实现 Comparable |
// PECS 原则示例
public void copy(List<? extends Number> src, List<? super Number> dest) {
for (Number n : src) { // 安全读取(生产者 extends)
dest.add(n); // 安全写入(消费者 super)
}
}
6.4 类型擦除
泛型信息仅在编译期存在,运行时会被擦除:
List<String> list1 = new ArrayList<>();
List<Integer> list2 = new ArrayList<>();
System.out.println(list1.getClass() == list2.getClass()); // true(都是 ArrayList.class)
类型擦除的后果:① 不能
instanceof泛型类型(如obj instanceof List<String>编译错误);② 不能创建泛型数组(new T[5]编译错误);③ 不能实例化类型参数(new T()编译错误);④ 静态成员不能使用类的类型参数。
6.5 本周实践任务
- [ ] 实现一个泛型工具方法:交换数组中任意两个位置的元素
- [ ] 用 PECS 原则实现
copy方法,理解extends和super的区别 - [ ] 用反射验证类型擦除:获取
ArrayList<String>和ArrayList<Integer>的 Class 对象比较 - [ ] 理解泛型不能重载的场景:
void method(List<String> list)和void method(List<Integer> list)编译报错
第七阶段:集合框架
[JDK 1.2+](核心接口),[JDK 5+](泛型化)。这是面试和工作中最核心的数据结构知识。
7.1 集合框架总览
7.2 List
| 实现类 | 底层结构 | 随机访问 | 增删(非尾部) | 线程安全 | 适用场景 |
|---|---|---|---|---|---|
| ArrayList | Object[] 动态数组,扩容 1.5 倍 | O(1) | O(n) | ❌ | 频繁随机读取 |
| LinkedList | 双向链表 | O(n) | O(1) | ❌ | 频繁头尾增删 |
| Vector | Object[] + synchronized | O(1) | O(n) | ✅ | 已过时,用 CopyOnWriteArrayList |
| CopyOnWriteArrayList | 写时复制数组 | O(1) | O(n) 写 | ✅ | 读多写少 |
List<Integer> list = new ArrayList<>();
list.add(1); // 尾部追加
list.add(0, 0); // 指定位置插入
list.get(0); // 按索引访问
list.remove(0); // 按索引删除
list.remove(Integer.valueOf(1));// 按元素删除
list.contains(1); // 是否包含
list.sort(Comparator.reverseOrder()); // 排序
// List 转数组
Integer[] arr = list.toArray(new Integer[0]);
ArrayList 扩容机制(高频面试考点):
- 默认初始容量 10
- 新增时若容量不足,扩容为原来的 1.5 倍(
oldCapacity + (oldCapacity >> 1)) - 使用
Arrays.copyOf()将原数组元素复制到新数组 - 可选:
new ArrayList<>(预估容量)减少扩容次数
7.3 Set
| 实现类 | 底层结构 | 有序性 | null | 适用场景 |
|---|---|---|---|---|
| HashSet | HashMap(key=元素, value=PRESENT 常量) | 无序 | ✅ 1 个 | 去重、快速查找 |
| LinkedHashSet | LinkedHashMap | 插入顺序 | ✅ 1 个 | 去重且保持插入顺序 |
| TreeSet | TreeMap(红黑树) | 自然序/Comparator | ❌ | 有序集合、范围查找 |
Set 判断元素重复的依据是
hashCode()+equals()。存入 HashSet 的对象必须正确重写这两个方法。
7.4 Map
| 实现类 | 底层结构 | 有序性 | null 键 | null 值 | 线程安全 |
|---|---|---|---|---|---|
| HashMap | 数组+链表+红黑树(JDK 8+) | 无序 | ✅ 1 个 | ✅ | ❌ |
| LinkedHashMap | HashMap + 双向链表 | 插入/访问顺序 | ✅ 1 个 | ✅ | ❌ |
| TreeMap | 红黑树 | 键自然序/Comparator | ❌ | ✅ | ❌ |
| Hashtable | 数组+链表(加锁) | 无序 | ❌ | ❌ | ✅ |
| ConcurrentHashMap | 分段/节点锁 + CAS | 无序 | ❌ | ❌ | ✅ |
Map<String, Integer> map = new HashMap<>();
map.put("Alice", 95);
map.get("Alice"); // 95
map.getOrDefault("Bob", 0); // 不存在返回 0
map.containsKey("Alice"); // true
map.putIfAbsent("Alice", 100); // 只有不存在时才放入
map.computeIfAbsent("Bob", k -> 0); // 不存在时计算放入
map.merge("Alice", 5, Integer::sum); // 合并值
// 遍历
map.forEach((k, v) -> System.out.println(k + " = " + v));
for (Map.Entry<String, Integer> entry : map.entrySet()) {
System.out.println(entry.getKey() + " = " + entry.getValue());
}
HashMap 底层原理(高频面试考点):
- JDK 7:数组 + 单向链表,头插法(并发下可能形成死循环)
- JDK 8+:数组 + 单链表 + 红黑树(链表长度 ≥ 8 且数组长度 ≥ 64 时树化),尾插法
- 默认初始容量 16,负载因子 0.75
- 扩容为原来的 2 倍
hash()方法:(h = key.hashCode()) ^ (h >>> 16)(高 16 位与低 16 位异或,减少碰撞)
7.5 Collections 工具类
Collections.sort(list); // 排序(要求元素实现 Comparable)
Collections.sort(list, comparator); // 自定义排序
Collections.reverse(list); // 反转
Collections.shuffle(list); // 随机打乱
Collections.max(list); // 最大值
Collections.min(list); // 最小值
Collections.binarySearch(list, key); // 二分查找(前提:已排序)
Collections.synchronizedList(list); // 包装为线程安全(性能差)
Collections.unmodifiableList(list); // 包装为不可修改
List.of() / Set.of() / Map.of() [JDK 9+]:创建不可变集合。
List<String> list = List.of("a", "b", "c"); // 不可变,null 不允许
Map<String, Integer> map = Map.of("a", 1, "b", 2); // 最多 10 对键值
7.6 比较器
| 方式 | 接口 | 方法 | 调用方式 |
|---|---|---|---|
| 自然排序 | Comparable<T> | compareTo(T o) | Collections.sort(list) |
| 定制排序 | Comparator<T> | compare(T o1, T o2) | Collections.sort(list, comparator) |
// Comparable:类内部定义自然顺序
public class Student implements Comparable<Student> {
@Override public int compareTo(Student o) {
return this.age - o.age;
}
}
// Comparator:外部灵活定制(Lambda 简化)[JDK 8+]
students.sort(Comparator.comparing(Student::getAge)
.thenComparing(Student::getName)
.reversed());
7.7 Stream API
[JDK 8+] 函数式数据处理,链式操作,惰性求值。
List<String> names = students.stream()
.filter(s -> s.getScore() >= 60) // 过滤
.sorted(Comparator.comparing(Student::getScore).reversed()) // 排序
.skip(0).limit(10) // 分页
.map(Student::getName) // 转换
.collect(Collectors.toList()); // 收集
// 分组
Map<String, List<Student>> byGrade = students.stream()
.collect(Collectors.groupingBy(s -> s.getScore() >= 60 ? "及格" : "不及格"));
// 规约
int total = students.stream()
.mapToInt(Student::getScore).sum();
Optional<Student> top = students.stream()
.max(Comparator.comparing(Student::getScore));
Stream 操作分类:
| 操作类型 | 方法 | 说明 |
|---|---|---|
| 中间操作(惰性) | filter, map, flatMap, distinct, sorted, peek, limit, skip | 返回 Stream,不立即执行 |
| 终端操作(触发) | collect, forEach, reduce, count, anyMatch, allMatch, findFirst | 返回结果或 void,触发流水线执行 |
7.8 本周实践任务
- [ ] 对比 ArrayList 和 LinkedList 在头部插入 10 万条数据的耗时
- [ ] 实现一个简单的 LRU 缓存(用 LinkedHashMap 的 accessOrder 模式)
- [ ] 自定义类存入 HashSet,验证重写/不重写
hashCode()和equals()时的行为差异 - [ ] 用 Stream API 对 List 进行过滤、排序、分组、统计,对比传统 for 循环
- [ ] 研究 HashMap 在 JDK 7 和 JDK 8+ 中的实现差异(头插法 vs 尾插法、红黑树引入原因)
第八阶段:I/O 流
8.1 I/O 流体系
Java I/O 基于装饰器模式,分为字节流和字符流两大类。
| 分类 | 输入 | 输出 | 处理单位 |
|---|---|---|---|
| 字节流 | InputStream | OutputStream | 1 字节 |
| 字符流 | Reader | Writer | 1 字符(2 字节,自动编码) |
| 缓冲流 | BufferedInputStream / BufferedReader | BufferedOutputStream / BufferedWriter | 缓冲提升性能 |
| 转换流 | InputStreamReader | OutputStreamWriter | 字节 → 字符 桥梁 |
// 字节流文件复制
try (FileInputStream fis = new FileInputStream("src.txt");
FileOutputStream fos = new FileOutputStream("dest.txt")) {
byte[] buffer = new byte[8192];
int len;
while ((len = fis.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
}
// 字符缓冲流逐行读取
try (BufferedReader br = new BufferedReader(new FileReader("file.txt"))) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
}
NIO(New I/O)引入的核心组件 [JDK 1.4+]:
| 组件 | 说明 |
|---|---|
| Channel(通道) | 双向数据传输,FileChannel、SocketChannel |
| Buffer(缓冲区) | 数据容器,ByteBuffer、CharBuffer |
| Selector(选择器) | 单线程管理多个 Channel,实现非阻塞 I/O |
NIO.2(AIO) [JDK 7+]:
// Path 与 Files 工具(替代 File 类)
Path path = Paths.get("/tmp/test.txt");
List<String> lines = Files.readAllLines(path); // 一次性读所有行
Files.write(path, lines, StandardCharsets.UTF_8); // 一次性写入
Files.walk(Paths.get("/tmp")) // 递归遍历文件树
.filter(Files::isRegularFile)
.forEach(System.out::println);
8.2 序列化
// 实现 Serializable 接口标记可序列化
public class User implements Serializable {
private static final long serialVersionUID = 1L; // 版本号
private String name;
private transient String password; // transient 字段不参与序列化
}
// 序列化
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user.ser"))) {
oos.writeObject(user);
}
// 反序列化(不调用构造方法)
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.ser"))) {
User user = (User) ois.readObject();
}
serialVersionUID用于验证序列化兼容性。不显式声明时编译器自动生成,类修改后可能不兼容。
8.3 本周实践任务
- [ ] 用字节流和字符流分别复制文件,对比性能和适用场景
- [ ] 用
Files.walk()统计某个目录下所有.java文件的总行数 - [ ] 实现一个对象的序列化与反序列化,验证
transient字段被跳过 - [ ] 用
BufferedReader读取大文件(>1GB),观察内存占用
第九阶段:多线程与并发
这是 Java 进阶的核心,也是企业级开发和面试的重中之重。
9.1 线程基础
// 方式一:继承 Thread
class MyThread extends Thread {
@Override public void run() { System.out.println("子线程运行"); }
}
new MyThread().start();
// 方式二:实现 Runnable(推荐,解耦)
new Thread(() -> System.out.println("子线程运行")).start();
// 方式三:实现 Callable + Future(有返回值、可抛异常)
FutureTask<String> task = new FutureTask<>(() -> "结果");
new Thread(task).start();
String result = task.get(); // 阻塞等待结果
线程生命周期:
9.2 线程安全与锁
synchronized 关键字:
// 同步代码块
synchronized (lock) { ... }
// 同步实例方法(锁当前对象 this)
public synchronized void method() { ... }
// 同步静态方法(锁 Class 对象)
public static synchronized void staticMethod() { ... }
synchronized在 JDK 6 后经过重大优化(偏向锁、轻量级锁、锁粗化、锁消除),性能已大幅提升。
Lock 接口 [JDK 5+](java.util.concurrent.locks):
| 特性 | synchronized | Lock |
|---|---|---|
| 可中断获取 | ❌ | ✅ lockInterruptibly() |
| 尝试非阻塞获取 | ❌ | ✅ tryLock() |
| 超时获取 | ❌ | ✅ tryLock(time, unit) |
| 公平锁 | ❌ | ✅ new ReentrantLock(true) |
| 条件变量 | wait/notify | ✅ Condition(多个等待队列) |
| 自动释放 | ✅(代码块结束或异常) | ❌ 必须 unlock()(finally 中) |
ReentrantLock lock = new ReentrantLock();
try {
lock.lock();
// 临界区代码
} finally {
lock.unlock(); // 务必在 finally 中释放
}
9.3 线程池
[JDK 5+] java.util.concurrent.ExecutorService
阿里巴巴开发手册强制要求:线程池必须通过
ThreadPoolExecutor显式创建,禁止使用Executors工厂方法(newFixedThreadPool/newCachedThreadPool可能导致 OOM)。
ThreadPoolExecutor executor = new ThreadPoolExecutor(
4, // corePoolSize:核心线程数
10, // maximumPoolSize:最大线程数
60L, TimeUnit.SECONDS, // keepAliveTime:空闲线程存活时间
new LinkedBlockingQueue<>(100), // workQueue:任务队列
new ThreadPoolExecutor.CallerRunsPolicy() // rejectedExecutionHandler:拒绝策略
);
// 提交任务
executor.execute(() -> { ... }); // Runnable,无返回值
Future<String> future = executor.submit(() -> "result"); // Callable,有返回值
// 优雅关闭
executor.shutdown(); // 不再接受新任务,等待已提交任务完成
executor.awaitTermination(60, TimeUnit.SECONDS);
拒绝策略:
| 策略 | 行为 |
|---|---|
AbortPolicy(默认) | 抛 RejectedExecutionException |
CallerRunsPolicy | 由调用者线程执行 |
DiscardPolicy | 静默丢弃 |
DiscardOldestPolicy | 丢弃队列中最旧任务 |
9.4 并发工具类
| 工具 | 说明 | JDK |
|---|---|---|
| CountDownLatch | 等待 N 个线程完成 | 5 |
| CyclicBarrier | 所有线程到达屏障点后同时继续 | 5 |
| Semaphore | 控制同时访问资源的线程数(限流) | 5 |
| Exchanger | 两个线程间交换数据 | 5 |
// CountDownLatch 示例:主线程等待所有子任务完成
CountDownLatch latch = new CountDownLatch(5);
for (int i = 0; i < 5; i++) {
new Thread(() -> {
doWork();
latch.countDown();
}).start();
}
latch.await(); // 阻塞直到计数归零
9.5 并发集合
| 集合 | 原理 | 特点 |
|---|---|---|
| ConcurrentHashMap | JDK 7 分段锁;JDK 8 CAS + synchronized | 高并发读写,不锁整个 Map |
| CopyOnWriteArrayList | 写时复制整个数组 | 读多写少,读无锁 |
| BlockingQueue | 阻塞队列(生产者-消费者) | ArrayBlockingQueue / LinkedBlockingQueue |
// BlockingQueue 生产者-消费者模型
BlockingQueue<String> queue = new LinkedBlockingQueue<>(100);
// 生产者
queue.put(item); // 队列满时阻塞
// 消费者
String item = queue.take(); // 队列空时阻塞
9.6 原子类与 CAS
[JDK 5+] java.util.concurrent.atomic
| 原子类 | 对应类型 |
|---|---|
AtomicInteger / AtomicLong / AtomicBoolean | 基本类型 |
AtomicReference<T> | 引用类型 |
AtomicStampedReference<T> | 解决 ABA 问题(带版本号) |
LongAdder / DoubleAdder | 高并发计数(比 AtomicLong 吞吐更高)[JDK 8+] |
AtomicInteger counter = new AtomicInteger(0);
counter.incrementAndGet(); // 自增并返回(原子操作)
counter.compareAndSet(5, 10); // CAS:期望值 5 时设置为 10
CAS(Compare And Swap)原理:无锁乐观算法,CPU 指令级原子操作。问题:① ABA 问题(用版本号解决);② 自旋开销;③ 只能保证一个共享变量的原子操作。
9.7 ThreadLocal
为每个线程提供独立的变量副本,实现线程隔离(如数据库连接、Session 管理、事务上下文)。
ThreadLocal<SimpleDateFormat> formatter = ThreadLocal.withInitial(
() -> new SimpleDateFormat("yyyy-MM-dd")
);
String date = formatter.get().format(new Date());
// 使用后应在 finally 中 remove() 防止内存泄漏
formatter.remove();
ThreadLocal 的 key 是弱引用,但 value 是强引用。若 ThreadLocal 对象被 GC 但线程未销毁,value 无法被回收 → 内存泄漏。务必在 finally 中
remove()。
9.8 虚拟线程(Virtual Thread)
[JDK 19 预览,JDK 21 正式]
虚拟线程是轻量级用户态线程,由 JVM 调度而非 OS 调度,解决了平台线程数量限制和高并发下的线程切换开销问题。
// 创建虚拟线程
Thread vThread = Thread.ofVirtual().start(() -> {
System.out.println("虚拟线程运行");
});
// 通过 ExecutorService 使用
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
executor.submit(() -> { ... });
executor.submit(() -> { ... });
}
// try-with-resources 自动等待所有任务完成
虚拟线程特别适合 I/O 密集型、高并发场景,可与结构化并发
[JDK 21 预览]配合使用。
9.9 本周实践任务
- [ ] 用
ThreadPoolExecutor手动创建线程池,模拟任务提交、队列满、拒绝策略触发 - [ ] 用
CountDownLatch实现并发测试:N 个线程同时执行,主线程等待全部完成后统计耗时 - [ ] 实现生产者-消费者模式(BlockingQueue)
- [ ] 对比
synchronized和ReentrantLock在可中断获取锁时的行为差异 - [ ] 体验虚拟线程:创建 100 万个虚拟线程执行 sleep 操作,观察内存占用
第十阶段:反射与注解
10.1 反射(Reflection)
反射是框架(Spring、MyBatis、JUnit)的核心基础。
// 获取 Class 对象的三种方式
Class<?> clazz1 = String.class;
Class<?> clazz2 = "hello".getClass();
Class<?> clazz3 = Class.forName("java.lang.String"); // 可能抛 ClassNotFoundException
// 创建实例
Object obj = clazz1.getDeclaredConstructor().newInstance();
// 获取方法并调用
Method method = clazz1.getMethod("length");
int length = (int) method.invoke("hello");
// 获取字段并修改
Field field = clazz1.getDeclaredField("value"); // String 的 value[]
field.setAccessible(true); // 突破 private 限制
char[] value = (char[]) field.get("hello");
// 获取构造方法
Constructor<?> cons = clazz1.getConstructor(String.class);
String str = (String) cons.newInstance("hello");
反射的优缺点:灵活、动态性强(框架基础),但性能低、破坏封装、编译期安全检查失效。
10.2 注解(Annotation)
[JDK 5+],元数据标记。
内置注解:
| 注解 | 作用 |
|---|---|
@Override | 标记方法重写,编译期检查 |
@Deprecated | 标记已过时,不推荐使用 |
@SuppressWarnings | 抑制编译器警告 |
@SafeVarargs | 抑制可变参数的堆污染警告 |
@FunctionalInterface | 标记函数式接口 [JDK 8+] |
元注解(注解的注解):
| 元注解 | 作用 |
|---|---|
@Target | 指定注解可用位置(TYPE/METHOD/FIELD/PARAMETER 等) |
@Retention | 注解保留策略:SOURCE(源码) / CLASS(字节码) / RUNTIME(运行时) |
@Documented | 被注解的元素会包含在 Javadoc 中 |
@Inherited | 子类可继承父类的该注解 |
@Repeatable | 允许在同一元素上重复使用 [JDK 8+] |
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
String value() default "";
boolean recordTime() default true;
}
// 使用注解
@Log(value = "用户登录", recordTime = true)
public void login() { ... }
注解 + 反射 → 框架基础:Spring 通过扫描 @Component、@Autowired 等注解,结合反射实现 IoC 和 DI。
10.3 本周实践任务
- [ ] 用反射实现一个简单的 Bean 复制工具:遍历源对象字段,复制到目标对象同名字段
- [ ] 自定义
@NotNull和@MaxLength注解,用反射实现参数校验 - [ ] 用
getDeclaredMethod("xxx")调用私有方法,理解setAccessible(true)的作用 - [ ] 对比反射创建对象与
new创建对象的性能差异(循环 100 万次)
第十一阶段:JVM 基础
11.1 JVM 内存模型
| 区域 | 存储内容 | 线程共享 | GC | 异常 |
|---|---|---|---|---|
| 堆 | 对象实例、数组 | ✅ | ✅ 主要 GC 区域 | OutOfMemoryError |
元空间 [JDK 8+] | 类元信息、运行时常量池 | ✅ | ✅(Full GC 时) | OutOfMemoryError |
| 虚拟机栈 | 局部变量表、操作数栈、方法出口 | ❌ | ❌ | StackOverflowError |
| 程序计数器 | 当前线程执行的字节码行号 | ❌ | ❌ | 无 |
| 本地方法栈 | native 方法调用 | ❌ | ❌ | StackOverflowError |
JDK 7 及以前为永久代(PermGen);JDK 8+ 替换为元空间(MetaSpace),使用本地内存,默认无上限,减少 Full GC。
11.2 垃圾回收(GC)
判断对象是否存活:
- 引用计数法:无法解决循环引用(Java 不使用)
- 可达性分析(Root Searching):从 GC Roots 出发,不可达的对象判定为可回收
GC Roots 包含:虚拟机栈引用对象、静态变量引用对象、常量池引用对象、JNI 引用对象。
垃圾回收算法:
| 算法 | 原理 | 优缺点 |
|---|---|---|
| 标记-清除 | 标记存活对象 → 清除未标记 | 产生内存碎片 |
| 标记-复制 | 将存活对象复制到另一区域 | 无碎片,但浪费一半内存(适合新生代) |
| 标记-整理 | 标记存活对象 → 移动到一端 | 无碎片,但移动成本高(适合老年代) |
分代收集理论:
新生代 → Minor GC(频繁,标记-复制算法)
├── Eden 区(新对象分配)
├── Survivor 0(From)
└── Survivor 1(To)
↓ 对象熬过多次 Minor GC(默认 15 次)后晋升
老年代 → Major GC / Full GC(较少,标记-整理/清除算法)
常见垃圾回收器:
| 回收器 | 算法 | 特点 | 适用 |
|---|---|---|---|
| Serial | 标记-复制/整理 | 单线程,STW 长 | 客户端 |
| Parallel | 标记-复制/整理 | 多线程,吞吐量优先 | 后台计算 |
| CMS | 标记-清除 | 并发、低延迟(已废弃 JDK 14) | 低延迟应用 |
| G1 | 分区 + 标记-整理 | 可控停顿时间 | JDK 9+ 默认 |
ZGC [JDK 11+] | 染色指针 | 超低停顿(<1ms) | 大堆低延迟 |
Shenandoah [JDK 12+] | 转发指针 | 低停顿并发回收 | 大堆低延迟 |
11.3 类加载机制
类加载器(双亲委派模型):
双亲委派机制:加载类时先从父加载器逐级向上查找,父加载器找到则返回,防止核心类被篡改。
11.4 本周实践任务
- [ ] 用
-Xms和-Xmx设置堆大小,用jstat -gc观察 GC 情况 - [ ] 用
jmap -heap查看堆内存分配,识别新生代和老年代 - [ ] 制造 OOM 场景:不断
list.add(new byte[1024*1024])观察 GC 日志 - [ ] 写一个自定义 ClassLoader,从指定路径加载
.class文件 - [ ] 对比不同垃圾回收器(Serial vs G1)在大量对象创建时的 GC 日志差异
第十二阶段:Java 新版本特性速览
| 版本 | 核心特性 | 影响力 |
|---|---|---|
| Java 8 | Lambda、Stream、Optional、新日期 API、接口 default/static 方法 | ⭐⭐⭐⭐⭐ |
| Java 9 | 模块化(Project Jigsaw)、List.of()、接口 private 方法、JShell | ⭐⭐⭐⭐ |
| Java 10 | var 局部变量类型推断 | ⭐⭐⭐ |
| Java 11 (LTS) | HTTP Client、String.isBlank() / lines() / strip()、直接运行 Java 文件 | ⭐⭐⭐⭐ |
| Java 14 | Switch 表达式(正式)、Records(预览)、instanceof 模式匹配(预览)、文本块(正式) | ⭐⭐⭐⭐ |
| Java 16 | Records 正式、instanceof 模式匹配正式 | ⭐⭐⭐⭐ |
| Java 17 (LTS) | Sealed Class、增强的伪随机数生成器 | ⭐⭐⭐⭐ |
| Java 21 (LTS) | Virtual Thread、Record Pattern、Pattern Matching for switch 正式、SequencedCollection | ⭐⭐⭐⭐⭐ |
// Sealed Class [JDK 17+]
public sealed class Shape permits Circle, Rectangle, Triangle { }
public final class Circle extends Shape { }
// 限制哪些类可以继承 Shape,增强类型安全性
// Pattern Matching for switch [JDK 21 正式]
String result = switch (obj) {
case Integer i -> "整数: " + i;
case String s -> "字符串: " + s;
case null -> "空值";
default -> "未知类型";
};
附录 A:Java SE 面试高频考点
== 和 equals() 有什么区别?
==比较基本类型的值或引用类型的地址;equals()比较对象内容(Object 默认用==,需重写)。String 的equals()已重写为内容比较。String、StringBuilder、StringBuffer 的区别?
String 不可变,每次拼接产生新对象;StringBuilder 可变、非线程安全、性能最高;StringBuffer 可变、线程安全(synchronized)、性能次之。
ArrayList 和 LinkedList 的区别?
ArrayList 基于动态数组,随机访问 O(1),尾部增删 O(1),中间增删 O(n);LinkedList 基于双向链表,随机访问 O(n),头尾增删 O(1)。ArrayList 内存连续,LinkedList 额外存储前后指针。
HashMap 的底层原理?
JDK 8+:数组 + 链表 + 红黑树。通过 hash 确定桶位置,链表长度 ≥ 8 且数组长度 ≥ 64 时树化。默认容量 16,负载因子 0.75,扩容为 2 倍。
HashMap 和 ConcurrentHashMap 的区别?
HashMap 非线程安全;ConcurrentHashMap JDK 7 用分段锁,JDK 8 用 CAS + synchronized 锁单个桶节点,支持高并发读写。不允许 null 键和值。
重写(Override)和重载(Overload)的区别?
重写:子类重新定义父类方法(签名相同),运行时多态。重载:同类中方法名相同但参数列表不同,编译期决定。
抽象类和接口的区别?Java 8+ 有什么变化?
抽象类可以有构造方法和成员变量,单继承;接口不能有构造方法,变量只能是常量,可多实现。Java 8+ 接口支持 default 方法和 static 方法。
异常体系?Checked 和 Unchecked 的区别?
Throwable → Error(不可处理)+ Exception。Exception → RuntimeException(非受检,编译不强制处理)+ 其他(受检,必须 try-catch 或 throws)。
final、finally、finalize 的区别?
final:修饰类不能继承、方法不能重写、变量不可变;finally:try-catch 后一定执行的代码块;finalize():GC 前调用的方法(JDK 9 废弃)。Java 的值传递和引用传递?
Java 只有值传递。基本类型传值拷贝;引用类型传引用地址的拷贝(指向同一对象)。不可能通过方法参数修改外部引用的指向。
反射的原理和应用场景?
运行时获取类信息(Class 对象),可创建实例、调用方法、修改字段。应用于框架(Spring IoC、MyBatis ORM)、动态代理、IDE 自动补全。
什么是 Java 的内存泄漏?常见场景?
不再使用的对象无法被 GC 回收。常见场景:ThreadLocal 未 remove()、静态集合持有对象引用、未关闭的连接/流、内部类持有外部类引用。
synchronized 和 Lock 的区别?
synchronized 是 JVM 层面的关键字,自动释放锁;Lock 是 API 层面的接口(如 ReentrantLock),需手动释放,支持尝试获取、超时、可中断、公平锁。
volatile 关键字的作用?
① 保证变量可见性(一个线程修改,其他线程立即可见);② 禁止指令重排序。但不能保证原子性(如
i++非原子)。什么是 CAS?ABA 问题如何解决?
CAS(Compare And Swap)是无锁乐观算法。ABA 问题:值从 A 变成 B 再变回 A,CAS 误认为未变化。用版本号(AtomicStampedReference)解决。
ThreadLocal 的原理和内存泄漏问题?
每个 Thread 有一个 ThreadLocalMap,key 为弱引用 ThreadLocal,value 为强引用值。ThreadLocal 被 GC 后 key 变 null,但 value 仍在 ThreadLocalMap 中(除非线程销毁或手动 remove)。
JDK 动态代理和 CGLIB 代理的区别?
JDK 动态代理要求目标类实现接口,生成
$Proxy类;CGLIB 通过继承目标类生成子类,不能代理 final 类和方法。Spring AOP 会自动选择。
附录 B:核心 API 速查表
java.lang 包
| 类/接口 | 说明 |
|---|---|
Object | 所有类的根类 |
String | 不可变字符序列 |
StringBuilder / StringBuffer | 可变字符序列 |
Integer / Long / Double / Boolean 等 | 基本类型包装类 |
Math | 数学运算工具 |
Thread / Runnable | 线程 |
Throwable / Exception / Error | 异常体系 |
Class<T> | 类元信息(反射入口) |
System | 系统工具(标准 I/O、GC、属性) |
Enum | 枚举基类 |
java.util 包
| 类/接口 | 说明 |
|---|---|
Collection / List / Set / Queue | 集合接口 |
Map | 键值对接口 |
ArrayList / LinkedList | List 实现 |
HashSet / TreeSet / LinkedHashSet | Set 实现 |
HashMap / TreeMap / LinkedHashMap | Map 实现 |
Collections | 集合工具类 |
Arrays | 数组工具类 |
Comparator / Comparable | 比较器接口 |
Optional [JDK 8+] | 空值安全包装 |
Objects [JDK 7+] | 对象工具(空安全 equals/hash) |
Random / ThreadLocalRandom | 随机数 |
java.util.concurrent 包 [JDK 5+]
| 类/接口 | 说明 |
|---|---|
ExecutorService / ThreadPoolExecutor | 线程池 |
ConcurrentHashMap | 并发 Map |
CopyOnWriteArrayList | 写时复制 List |
BlockingQueue / ArrayBlockingQueue | 阻塞队列 |
CountDownLatch / CyclicBarrier | 同步工具 |
Semaphore | 信号量 |
AtomicInteger / AtomicReference | 原子类 |
ReentrantLock | 可重入锁 |
CompletableFuture [JDK 8+] | 异步编排 |
java.io / java.nio 包
| 类 | 说明 |
|---|---|
File / Path [JDK 7+] | 文件路径 |
InputStream / OutputStream | 字节流 |
Reader / Writer | 字符流 |
BufferedReader / BufferedWriter | 缓冲字符流 |
ObjectInputStream / ObjectOutputStream | 序列化 |
Files [JDK 7+] | 文件工具(NIO.2) |
java.time 包 [JDK 8+]
| 类 | 说明 |
|---|---|
LocalDate / LocalTime / LocalDateTime | 日期时间 |
Instant | 时间戳 |
Duration / Period | 时间间隔 |
DateTimeFormatter | 格式化器 |
java.util.stream 包 [JDK 8+]
| 类 | 说明 |
|---|---|
Stream<T> | 数据流操作 |
Collectors | 收集器(toList、groupingBy 等) |
IntStream / LongStream / DoubleStream | 数值流 |
附录 C:学习资源与工具
- Oracle 官方教程:https://docs.oracle.com/javase/tutorial/
- Java SE 规范:https://docs.oracle.com/javase/specs/
- JDK 发行说明:https://www.oracle.com/java/technologies/javase/jdk-relnotes-index.html
- OpenJDK:https://openjdk.org/
- IntelliJ IDEA:Java 开发首选 IDE(社区版免费)
- VisualVM / JConsole:JVM 监控工具
- Arthas:阿里巴巴开源的 Java 诊断工具
- JMH(Java Microbenchmark Harness):微基准测试框架
版本说明:本教程以 Java SE 8 为基线,标记
[JDK x+]的新特性表示自该版本起可用。当前最新 LTS 为 Java 21(2023.09),建议学习和项目开发使用 Java 17 或 21。