Appearance
Advanced of Java
注解、泛型、Lambda、Stream API、反射、并发、JVM
泛型(generics)
java
// 泛型类
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();
// 泛型方法
public <T> void printArray(T[] array) {
for (T item : array) {
System.out.println(item);
}
}
// 任意类型
List<?> unknownList;
// Number 或其子类(上界)
List<? extends Number> numbers;
// Integer 或其父类(下界)
List<? super Integer> integers;反射(Reflection)
反射是 Java 在运行时动态获取类的信息并操作类对象的能力。它打破了 Java 的封装性,让程序可以:
- 在运行时知道任意对象的类信息
- 构造任意类的对象
- 调用任意对象的方法
- 访问和修改任意字段的值
核心类:java.lang.reflect 包下的 Class、Field、Method、Type、Constructor、Annotation 等
一些用例:
Spring 依赖注入
java
public class DependencyInjector {
public void inject(Object obj) throws Exception {
Class<?> clazz = obj.getClass();
// 扫描所有字段,找到 @Autowired 注解的
for (Field field : clazz.getDeclaredFields()) {
if (field.isAnnotationPresent(Autowired.class)) {
field.setAccessible(true);
// 从容器中获取 Bean 并注入
Object bean = getBeanFromContainer(field.getType());
field.set(obj, bean);
}
}
}
}动态代理(AOP 实现基础)
java
// JDK 动态代理(基于反射)
public interface UserService {
void addUser();
}
public class UserServiceImpl implements UserService {
public void addUser() {
System.out.println("添加用户");
}
}
// 创建代理
UserService proxy = (UserService) Proxy.newProxyInstance(
UserService.class.getClassLoader(),
new Class<?>[] { UserService.class },
(proxyObj, method, args) -> {
System.out.println("前置通知");
Object result = method.invoke(new UserServiceImpl(), args);
System.out.println("后置通知");
return result;
}
);
// 实现原方法前后切入其他动作
proxy.addUser();序列化/反序列化(JSON 转化)
java
public <T> T jsonToObject(String json, Class<T> clazz) throws Exception {
T instance = clazz.getDeclaredConstructor().newInstance();
// json to map
Map<String, Object> map = parseJson(json);
for (Field field : clazz.getDeclaredFields()) {
field.setAccessible(true);
String fieldName = field.getName();
if (map.containsKey(fieldName)) {
field.set(instance, map.get(fieldName));
}
}
return instance;
}测试单元(JUnit )
java
public class TestRunner {
public static void runTests(Class<?> testClass) throws Exception {
Object instance = testClass.getDeclaredConstructor().newInstance();
for (Method method : testClass.getDeclaredMethods()) {
if (method.isAnnotationPresent(Test.class)) {
// 执行 @Test 标记的方法
method.invoke(instance);
}
if (method.isAnnotationPresent(BeforeEach.class)) {
// 执行前置方法
method.invoke(instance);
}
}
}
}配置文件驱动(解耦)
java
// config.properties
// driver.class=com.mysql.cj.jdbc.Driver
// service.class=com.example.UserServiceImpl
Properties props = new Properties();
props.load(new FileInputStream("config.properties"));
// 动态加载类
Class<?> driverClass = Class.forName(props.getProperty("driver.class"));
Driver driver = (Driver) driverClass.getDeclaredConstructor().newInstance();
Class<?> serviceClass = Class.forName(props.getProperty("service.class"));
Object service = serviceClass.getDeclaredConstructor().newInstance();流(Stream)
Stream 是元素的序列,支持串行或并行的聚合操作:
- 不存储数据 - 元素来自数据源(集合、数组、I/O 等)
- 不改变源数据 - 返回新 Stream 或结果
- 惰性求值 - 中间操作延迟到终止操作才执行
- 一次消费 - 遍历后不能重复使用
parallel stream 数据量大(通常 > 10,000)时考虑并行
创建
java
IntStream intStream = Arrays.asList(1, 2, 3).stream();
Stream<String> stream3 = Stream.of("a", "b", "c");
IntStream intStream = IntStream.range(1, 5); // 1,2,3,4
IntStream intStream = IntStream.rangeClosed(1, 5); // 1,2,3,4,5
// 无限流(配合 limit 使用)
Stream<Integer> infinite = Stream.iterate(0, n -> n + 2); // 0,2,4,6...
Stream<Double> randoms = Stream.generate(Math::random);中间操作
java
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 2, 3);
// 条件过滤
numbers.stream()
.filter(n -> n % 2 == 0) // 偶数
.forEach(System.out::println); // 2,4,6,2
// 去重
numbers.stream()
.distinct()
.collect(Collectors.toList()); // [1,2,3,4,5,6]
// 截取
numbers.stream()
.limit(3)
.collect(Collectors.toList()); // [1,2,3]
// 跳过
numbers.stream()
.skip(3)
.collect(Collectors.toList()); // [4,5,6,2,3]
// 类似分页
numbers.stream()
.skip(3)
.limit(3)
.collect(Collectors.toList()); // [4,5,6]映射
java
class User {
int age;
String name;
}
List<User> arr;
List<String> names = arr.stream()
.map(User::getName)
.collect(Collector.toList());
// 扁平化
// 将 [[1,2], [3,4], [5,6]] 扁平化为 [1,2,3,4,5,6]
List<Integer> arr = Arrays.asList(
Arrays.asList(1, 2),
Arrays.asList(3, 4),
Arrays.asList(5, 6)
).stream()
.flatMap(List::stream)
.collect(Collectors.toList());
// ["Hello World", "Java Stream"] -> [Hello, World, Java, Stream]
List<String> arr = Arrays.asList("Hello World", "Java Stream").stream()
.flatMap(s -> Arrays.stream(s.split(" ")))
.collect(Collectors.toList());排序
java
// 自然排序 [apple, banana, cherry, date]
Arrays.asList("banana", "apple", "cherry", "date").stream()
.sorted()
.collect(Collectors.toList());
// 自定义比较器
// 倒序
sorted(Comparator.reverseOrder())
// 按长度
sorted(Comparator.comparing(String::length))
// 多字段排序
sorted(...)
.thenComparing(..);遍历与匹配
java
List<Integer> arr = Arrays.asList(1, 2, 3, 4, 5);
// forEach - 遍历
arr.stream().forEach(System.out::println);
// 匹配操作(短路求值)
boolean anyEven = arr.stream().anyMatch(n -> n % 2 == 0);
boolean allPositive = arr.stream().allMatch(n -> n > 0);
boolean noNegative = arr.stream().noneMatch(n -> n < 0);
// findFirst / findAny
Optional<Integer> first = arr.stream()
.filter(n -> n > 3)
.findFirst();
// // 并行时可能返回 4 或 5
Optional<Integer> any = arr.parallelStream()
.filter(n -> n > 3)
.findAny();归约
java
List<Integer> arr = Arrays.asList(1, 2, 3, 4, 5);
// 求和
int sum1 = arr.stream().reduce(0, (a, b) -> a + b);
int sum2 = arr.stream().reduce(0, Integer::sum);
Optional<Integer> sum3 = arr.stream().reduce(Integer::sum);
// 乘积
int product = arr.stream().reduce(1, (a, b) -> a * b);
// 字符串连接
String joined = Stream.of("a", "b", "c")
.reduce("", String::concat);
// 最大值/最小值
Optional<Integer> max = arr.stream().reduce(Integer::max);
// 复杂归约:统计
int[] stats = arr.stream().reduce(
new int[]{0, 0},
(acc, n) -> new int[]{acc[0] + n, acc[1] + 1},
(a, b) -> new int[]{a[0] + b[0], a[1] + b[1]}
);
double avg = (double) stats[0] / stats[1];收集器
java
class User {
int age;
String gender;
String name;
}
List<User> arr;
// toList、toSet、toCollection
LinkedHashSet set = arr.stream().collect(Collector.toCollection(LinkedHashSet::new));
// toMap
// joining
String str = arr.stream()
.map(User::getName)
.collect(Collectors.joining(", ", "[", "]"));
// groupingBy
Map<String, List<User>> byGender = users.stream()
.collect(Collectors.groupingBy(User::getGender));
// 分组聚合
Map<String, Double> avgAgeByGender = arr.stream()
.collect(Collectors.groupingBy(
User::getGender,
Collectors.averagingInt(User::getAge)
));
// 分区-partitingBy
Map<Boolean, List<User>> partitioned = arr.stream()
.collect(Collectors.partitioningBy(u -> u.getAge() >= 30));
// 多级分区
Map<String, Map<Integer, List<User>>> multiLevel = arr.stream()
.collect(Collectors.groupingBy(
User::getGender,
Collectors.groupingBy(User::getAge)
));
// 统计
IntSummaryStatistics stats = arr.stream()
.collect(Collectors.summarizingInt(User::getAge));
// count=4, sum=118, min=25, average=29.5, max=35
// 聚合-reducing
Integer totalAge = arr.stream()
.collect(Collectors.reducing(0, User::getAge, Integer::sum));并发
| 特性 | 进程 (Process) | 线程 (Thread) |
|---|---|---|
| 资源占用 | 独立地址空间,资源开销大 | 共享进程资源,开销小 |
| 通信方式 | IPC(管道、套接字、共享内存等) | 直接共享内存,需同步机制 |
| 切换开销 | 大(需切换页表) | 小(只需保存寄存器) |
线程状态
Java 线程有 6 种状态(定义在 java.lang.Thread.State 枚举中)
NEW -> RUNNABLE -> (BLOCKED/WAITING/TIMED_WAITING) -> TERMINATED
plain
┌───────┐
│ NEW │
└──┬────┘
│ start()
▼
┌─────────┐ ┌──────────┐
┌────┤ RUNNABLE├────►│ RUNNING │◄────┐
│ └────┬────┘ └──────────┘ │
│ │ │
│ ┌────┴────┐ │
│ │ │ │
│ ▼ ▼ │
│ ┌───────┐ ┌─────────┐ │
└─┤BLOCKED│ │ WAITING │◄──────────────┘
└───┬───┘ └────┬────┘ notify()/notifyAll()
│ │
│ ┌─────┴─────────┐
│ │ TIMED_WAITING │
│ └─────┬─────────┘
│ │
└──────────┘
│ 获取锁 / 被唤醒 / 超时
▼
┌──────────┐
│TERMINATED│
└──────────┘生命周期
Thread t = new Thread(() -> {});
| 方法 | 作用 | 状态变化 |
|---|---|---|
t.start() | 启动线程 | NEW → RUNNABLE |
t.join() | 等待该线程执行完毕 | RUNNABLE → WAITING/TIMED_WAITING |
Thread.yield() | 让出 CPU,进入就绪态 | RUNNABLE → RUNNABLE(可能) |
Thread.sleep(long) | 休眠指定毫秒,不释放锁 | RUNNABLE → TIMED_WAITING → RUNNABLE |
中断与终止
| 方法 | 作用 | 注意 |
|---|---|---|
t.interrupt() | 设置中断标志位 | 不会真正停止线程,只是发信号 |
t.isInterrupted() | 检查中断状态 | 不清除标志位 |
Thread.interrupted() | 检查并清除中断状态 | 静态方法 |
t.stop() | 已废弃 | 不安全,可能导致资源泄漏 |
t.suspend() | 已废弃 | 不安全,可能导致资源泄漏 |
t.resume() | 已废弃 | 不安全,可能导致资源泄漏 |
Object 等待与通知(同步块中使用)
| 方法 | 作用 | 状态变化 |
|---|---|---|
wait() | 释放锁,无限等待 | RUNNABLE → WAITING |
wait(long timeout) | 释放锁,限时等待 | RUNNABLE → TIMED_WAITING |
notify() | 唤醒一个等待线程 | WAITING → BLOCKED(需重新获取锁) |
notifyAll() | 唤醒所有等待线程 | WAITING → BLOCKED |
java
public class ThreadStateDemo {
public static void main(String[] args) throws Exception {
Object lock = new Object();
Thread t1 = new Thread(() -> {
synchronized (lock) {
try {
System.out.println("t1 进入 WAITING");
lock.wait();
System.out.println("t1 被唤醒,尝试获取锁...");
// 这里可能 BLOCKED,如果 t2 还没释放锁
Thread.sleep(1000); // TIMED_WAITING
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("t1 结束");
});
Thread t2 = new Thread(() -> {
synchronized (lock) {
System.out.println("t2 持有锁,准备唤醒 t1");
// 唤醒 t1,但 t1 需等待 t2 释放锁
lock.notify();
try {
// 模拟耗时操作,t1 此时是 BLOCKED
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t2 释放锁");
}
});
System.out.println("t1 NEW: " + t1.getState());
t1.start();
// 确保 t1 先执行
Thread.sleep(100);
// WAITING
System.out.println("t1 WAITING: " + t1.getState());
t2.start();
// BLOCKED(等待 t2 释放)
System.out.println("t1 BLOCKED: " + t1.getState()); 锁)
// 等待 t1 执行完毕
t1.join();
// TERMINATED
System.out.println("t1 TERMINATED: " + t1.getState());
}
}多线程
java
// 1. 继承 Thread 类(不推荐,单继承限制)
class MyThread extends Thread {
@Override
public void run() {
System.out.println("线程运行");
}
}
MyThread t1 = new MyThread();
t1.start();
// 2. 实现 Runnable 接口(推荐)
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("线程运行");
}
}
Thread t2 = new Thread(new MyRunnable());
t2.start();
// 3. 实现 Callable 接口(有返回值)
class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
return "任务完成";
}
}
FutureTask<String> futureTask = new FutureTask<>(new MyCallable());
new Thread(futureTask).start();
String result = futureTask.get(); // 阻塞获取结果
// Lambda 简化(Java 8+)
new Thread(() -> System.out.println("线程运行")).start();线程同步
为了避免多个线程同时访问共享资源时导致数据不一致的问题。由于线程的调度由操作系统决定,多个线程可能在任意时刻被中断或切换,因此需要通过同步机制来确保线程安全。
线程安全 线程同步的核心是原子性,即一组操作必须不可分割地执行,其他线程在此期间无法干扰。为实现这一点,通常使用锁机制来保护共享资源。加锁的代码块称为临界区,在同一时刻只能有一个线程进入。
synchronize 关键字
Java 中实现线程同步的关键字,她可以确保多个线程在访问共享资源时的互斥性(mutual exclusion),从而避免数据不一致和线程安全问题。
底层机制:
- 基于 Monitor(监视器锁)实现
- 每个 Java 对象都有一个关联的 Monitor
- 通过 monitorenter/monitorexit 指令实现锁的获取与释放
特性:
- 可重入性:同一线程可多次获取同一把锁
- 独占性:同一时间只有一个线程持有锁
- 不可中断性:等待锁的线程无法被中断
- 自动释放:代码执行完毕或异常时自动释放锁
- 非公平锁:默认非公平,新线程可能插队获取锁
对象头结构:Mark Word 中存储锁状态
plain
无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁CAS, Compare-And-Swap1
注意事项:
- 避免锁范围过大,只锁必要代码块。
- 避免锁嵌套,防止死锁。
- 锁对象使用 final,防止对象被修改。
- 不要使用 String/Integer 等常量作为锁, 防止锁对象共享。
- 静态和非静态锁不同,类锁和实例锁。
java
// 同步实例方法,锁 this
public synchronized void fun() {}
// 同步静态方法,锁 Class 对象
public static synchronized void fun() {}
public void fun() {
// 同步块,指定对象。
synchronized(obj) {}
}
// 推荐写法
public class OptimizedCounter {
private int count = 0;
private final Object lock = new Object();
public void increment() {
synchronized(lock) {
count++;
}
}
public void decrement() {
// 或者锁对象使用当前实例
synchronized (this) {
count --;
}
}
public int getCount() {
synchronized (lock) {
return count;
}
}
}嵌套死锁
plain
时间线:
─────────────────────────────────────────────────────
Thread-1 Thread-2
│ │
▼ ▼
synchronized(a) synchronized(b) ← 各自获取锁
│ │
▼ ▼
synchronized(b) synchronized(a) ← 互相等待对方
│ │
▼ ▼
阻塞 ◄──────────────► 阻塞 ← 死锁!
─────────────────────────────────────────────────────java
public class Account {
private int balance;
private final String name;
public Account(String name, int balance) {
this.name = name;
this.balance = balance;
}
public void transfer(Account target, int amount) {
synchronized(this) {
synchronized (target) {
this.balance -= amount;
target.balance += amount;
}
}
}
public static void main(String[] args) {
Account a = new Account("A", 1000);
Account b = new Accoubt("B", 1000);
new Thread(() -> a.transfer(b, 100), "thread-1").start();
new Thread(() -> b.transter(a, 200), "thread-2").start();
// thread-1 持有 a 的锁,等待 b 的锁。
// thread-2 持有 b 的锁,等待 a 的锁。
}
}锁顺序一致(推荐)
java
public void transfer(Account target, int amount) {
Object lock1 = this.id > target.id? this : target;
Object lock2 = lock1 == this ? target : this;
syncronized(lock1) {
syncronized(lock2) {
this.balance -= amount;
target.balance += amount;
}
}
}trylock
java
public class Account {
private final ReentrantLock lock = new ReentrantLock();
private int balance;
public Amount(int balance) {
this.balance = balance;
}
public boolean transfer(Account target, int amount, long timeout) throws InterruptedExcetion {
boolean acquired = false;
try {
acquired = this.lock.trylock(timeout, TimeUnit.MILLSECOUND);
if (!acquired) return false;
acquired = target.lock.trylock(timeout, TimeUnit.MILLSECOUND);
if (!acquired) return false;
this.balance -= amount;
target.balance += amount;
return true;
} finally {
// 注意顺序
if (acquired) target.lock.unlock();
this.lock.unlock();
}
}
}全局锁
java
private static final Object GLOBAL_LOCK = new Object();
public void transfer(Account target, int amount) {
syncronized(GLOBAL_LOCK) {
this.balance -= amount;
target.balance += amount;
}
}CAS, Compare-And-Swap
CAS 核心原理
plain
// 伪代码
// v:内存地址中的实际值(Value)
// a: 预期值(Expected Value)
// b: 新值(New Value)
boolean compareAndSwap(v, a, b) {
if (v == a) {
v = b;
return true;
}
return false;
}硬件层面支持
CAS 是原子指令,由 CPU 直接支持:
| 架构 | 指令 | 说明 |
|---|---|---|
| x86 | CMPXCHG | 比较并交换 |
| ARM | LDREX | 独占加载 |
| ARM | STREX | 存储 |
| RISC-V | LR | 保留加载 |
| RISC-V | SC | 条件存储 |
CAS 乐观锁
synchronized 自旋
锁升级
第一阶段:无锁
锁对象刚创建private Object lock = new Object(); 此时,对象头:哈希码(25)+分代年龄(4)+0(偏向锁位)+01(锁标志)
- 偏向锁位 = 0,表示可偏向但为偏向任何线程
- 锁标志 = 01
第二阶段:偏向锁
plain
第一次获取锁:
对象头:无锁状态(可偏向)
↓
CAS 操作:线程ID → 对象头
↓
成功 → 偏向锁状态
后续同线程进入:
检查对象头线程ID == 当前线程ID
↓
相等 → 直接执行(无需任何同步操作)
不等 → 尝试 CAS 替换(轻量级锁升级)
优势:单线程场景零开销第三阶段:轻量级锁
第四阶段:重量级锁
| JVM 参数 | 默认值 | 说明 |
|---|---|---|
-XX:+UseBiasedLocking | true (JDK8+) | 启用偏向锁 |
-XX:BiasedLockingStartupDelay=4000 | 4000ms | 偏向锁延迟开启 |
-XX:+UseSpinning | true | 启用轻量级锁自旋 |
-XX:PreBlockSpin=10 | 10 | 自旋次数阈值 |
-XX:+UseAdaptiveSpinning | true | 自适应自旋 |
-XX:-UseBiasedLocking | - | 禁用偏向锁(高并发建议) |
plain
┌─────────────────────────────────────────┐
│ 锁升级规则速查 │
├─────────────────────────────────────────┤
│ │
│ 无锁 → 偏向锁: │
│ • 第一个线程进入同步块 │
│ • 且偏向锁已开启(4秒延迟后) │
│ │
│ 偏向锁 → 轻量级锁: │
│ • 另一个线程尝试获取锁 │
│ • CAS 替换线程ID失败 │
│ │
│ 轻量级锁 → 重量级锁: │
│ • 自旋超过阈值(默认10次) │
│ • 或自适应计算值 │
│ • 或等待线程数超过1个 │
│ │
│ ❌ 不会降级! │
│ 重量级锁释放后仍为重量级锁 │
│ 除非 GC 回收对象 │
│ │
└─────────────────────────────────────────┘Java 1.5+
- java.util.concurrent.locks.Lock
- java.util.concurrent.automic
- java.util.concurrent.