Skip to content

Java 核心特性笔记 ​

1. 集合框架 ​

1.1 什么是集合框架? ​

Java 集合框架是用于存储和操作数据的一组接口和类,位于 java.util 包中。

1.2 集合框架体系 ​

  • Collection 接口:存储单个元素的集合
    • List:有序、可重复
    • Set:无序、不可重复
    • Queue:队列,先进先出
  • Map 接口:存储键值对的集合

1.3 常用集合类 ​

1.3.1 List 接口 ​

ArrayList:基于动态数组实现,查询快,增删慢

java
// 创建 ArrayList
List<String> arrayList = new ArrayList<>();

// 添加元素
arrayList.add("Java");
arrayList.add("Python");
arrayList.add("C++");

// 获取元素
String element = arrayList.get(0); // "Java"

// 修改元素
arrayList.set(1, "JavaScript");

// 删除元素
arrayList.remove(2);

// 遍历
for (String item : arrayList) {
    System.out.println(item);
}
// 创建 ArrayList
List<String> arrayList = new ArrayList<>();

// 添加元素
arrayList.add("Java");
arrayList.add("Python");
arrayList.add("C++");

// 获取元素
String element = arrayList.get(0); // "Java"

// 修改元素
arrayList.set(1, "JavaScript");

// 删除元素
arrayList.remove(2);

// 遍历
for (String item : arrayList) {
    System.out.println(item);
}

LinkedList:基于双向链表实现,查询慢,增删快

java
List<String> linkedList = new LinkedList<>();
linkedList.add("Java");
linkedList.addFirst("Python"); // 添加到开头
linkedList.addLast("C++"); // 添加到结尾
String first = linkedList.getFirst(); // 获取第一个元素
String last = linkedList.getLast(); // 获取最后一个元素
List<String> linkedList = new LinkedList<>();
linkedList.add("Java");
linkedList.addFirst("Python"); // 添加到开头
linkedList.addLast("C++"); // 添加到结尾
String first = linkedList.getFirst(); // 获取第一个元素
String last = linkedList.getLast(); // 获取最后一个元素

1.3.2 Set 接口 ​

HashSet:基于哈希表实现,无序,不允许重复

java
Set<String> hashSet = new HashSet<>();
hashSet.add("Java");
hashSet.add("Python");
hashSet.add("Java"); // 重复元素,不会添加
boolean contains = hashSet.contains("Java"); // true
hashSet.remove("Python");
Set<String> hashSet = new HashSet<>();
hashSet.add("Java");
hashSet.add("Python");
hashSet.add("Java"); // 重复元素,不会添加
boolean contains = hashSet.contains("Java"); // true
hashSet.remove("Python");

LinkedHashSet:基于哈希表+链表实现,保持插入顺序

java
Set<String> linkedHashSet = new LinkedHashSet<>();
linkedHashSet.add("Java");
linkedHashSet.add("Python");
linkedHashSet.add("C++");
// 遍历顺序:Java, Python, C++
Set<String> linkedHashSet = new LinkedHashSet<>();
linkedHashSet.add("Java");
linkedHashSet.add("Python");
linkedHashSet.add("C++");
// 遍历顺序:Java, Python, C++

TreeSet:基于红黑树实现,有序(自然排序或自定义排序)

java
Set<String> treeSet = new TreeSet<>();
treeSet.add("Java");
treeSet.add("Python");
treeSet.add("C++");
// 遍历顺序:C++, Java, Python(自然排序)

// 自定义排序
Set<Integer> customTreeSet = new TreeSet<>((a, b) -> b - a); // 降序
customTreeSet.add(3);
customTreeSet.add(1);
customTreeSet.add(2);
// 遍历顺序:3, 2, 1
Set<String> treeSet = new TreeSet<>();
treeSet.add("Java");
treeSet.add("Python");
treeSet.add("C++");
// 遍历顺序:C++, Java, Python(自然排序)

// 自定义排序
Set<Integer> customTreeSet = new TreeSet<>((a, b) -> b - a); // 降序
customTreeSet.add(3);
customTreeSet.add(1);
customTreeSet.add(2);
// 遍历顺序:3, 2, 1

1.3.3 Map 接口 ​

HashMap:基于哈希表实现,无序,键不允许重复

java
Map<String, Integer> hashMap = new HashMap<>();

// 添加键值对
hashMap.put("Java", 1);
hashMap.put("Python", 2);
hashMap.put("C++", 3);

// 获取值
Integer value = hashMap.get("Java"); // 1

// 检查键是否存在
boolean containsKey = hashMap.containsKey("Java"); // true

// 删除键值对
hashMap.remove("Python");

// 遍历
// 方式1:遍历键
for (String key : hashMap.keySet()) {
    System.out.println(key + ": " + hashMap.get(key));
}

// 方式2:遍历键值对
for (Map.Entry<String, Integer> entry : hashMap.entrySet()) {
    System.out.println(entry.getKey() + ": " + entry.getValue());
}

// 方式3:遍历值
for (Integer val : hashMap.values()) {
    System.out.println(val);
}
Map<String, Integer> hashMap = new HashMap<>();

// 添加键值对
hashMap.put("Java", 1);
hashMap.put("Python", 2);
hashMap.put("C++", 3);

// 获取值
Integer value = hashMap.get("Java"); // 1

// 检查键是否存在
boolean containsKey = hashMap.containsKey("Java"); // true

// 删除键值对
hashMap.remove("Python");

// 遍历
// 方式1:遍历键
for (String key : hashMap.keySet()) {
    System.out.println(key + ": " + hashMap.get(key));
}

// 方式2:遍历键值对
for (Map.Entry<String, Integer> entry : hashMap.entrySet()) {
    System.out.println(entry.getKey() + ": " + entry.getValue());
}

// 方式3:遍历值
for (Integer val : hashMap.values()) {
    System.out.println(val);
}

LinkedHashMap:基于哈希表+链表实现,保持插入顺序

java
Map<String, Integer> linkedHashMap = new LinkedHashMap<>();
linkedHashMap.put("Java", 1);
linkedHashMap.put("Python", 2);
linkedHashMap.put("C++", 3);
// 遍历顺序:Java, Python, C++
Map<String, Integer> linkedHashMap = new LinkedHashMap<>();
linkedHashMap.put("Java", 1);
linkedHashMap.put("Python", 2);
linkedHashMap.put("C++", 3);
// 遍历顺序:Java, Python, C++

TreeMap:基于红黑树实现,键有序

java
Map<String, Integer> treeMap = new TreeMap<>();
treeMap.put("Java", 1);
treeMap.put("Python", 2);
treeMap.put("C++", 3);
// 遍历顺序:C++, Java, Python(按键排序)
Map<String, Integer> treeMap = new TreeMap<>();
treeMap.put("Java", 1);
treeMap.put("Python", 2);
treeMap.put("C++", 3);
// 遍历顺序:C++, Java, Python(按键排序)

ConcurrentHashMap:线程安全的 HashMap,适合多线程环境

java
Map<String, Integer> concurrentHashMap = new ConcurrentHashMap<>();
// 用法与 HashMap 类似,但线程安全
Map<String, Integer> concurrentHashMap = new ConcurrentHashMap<>();
// 用法与 HashMap 类似,但线程安全

1.4 HashMap 源码深度解析(面试重点) ​

1.4.1 数据结构 ​

JDK 1.8 HashMap 采用 数组 + 链表 + 红黑树:

数组(Node[] table)
  │
  ├── [0] → Node → Node → TreeNode → TreeNode(链表长度≥8且数组长度≥64时转红黑树)
  ├── [1] → null
  ├── [2] → Node → Node
  ├── ...
  └── [n-1]
数组(Node[] table)
  │
  ├── [0] → Node → Node → TreeNode → TreeNode(链表长度≥8且数组长度≥64时转红黑树)
  ├── [1] → null
  ├── [2] → Node → Node
  ├── ...
  └── [n-1]

Node 结构:

java
static class Node<K,V> {
    final int hash;    // key 的 hash 值
    final K key;
    V value;
    Node<K,V> next;    // 链表下一个节点
}
static class Node<K,V> {
    final int hash;    // key 的 hash 值
    final K key;
    V value;
    Node<K,V> next;    // 链表下一个节点
}

1.4.2 为什么容量必须是 2 的幂? ​

核心原因:用位运算代替取模,提高效率。

java
// 计算 key 在数组中的位置
// 方式一:取模(慢)
int index = hash % table.length;

// 方式二:位运算(快,HashMap 实际使用)
int index = (table.length - 1) & hash;
// 计算 key 在数组中的位置
// 方式一:取模(慢)
int index = hash % table.length;

// 方式二:位运算(快,HashMap 实际使用)
int index = (table.length - 1) & hash;

当 length = 2^n 时,(length - 1) & hash 等价于 hash % length,但位运算比取模快得多。

扩容时也有优势: 2 的幂扩容后,元素要么在原来位置,要么在 原位置 + 旧容量,不需要重新 hash。

1.4.3 扩容机制(JDK 1.7 vs 1.8) ​

JDK 1.7 头插法 — 存在死链问题:

java
// JDK 1.7 transfer() 简化
void transfer(Entry[] newTable) {
    for (Entry<K,V> e : table) {
        while (e != null) {
            Entry<K,V> next = e.next;  // 1. 记录下一个
            int i = indexFor(e.hash, newTable.length);
            e.next = newTable[i];       // 2. 头插:新节点指向当前头
            newTable[i] = e;            // 3. 新节点成为头
            e = next;                   // 4. 处理下一个
        }
    }
}
// JDK 1.7 transfer() 简化
void transfer(Entry[] newTable) {
    for (Entry<K,V> e : table) {
        while (e != null) {
            Entry<K,V> next = e.next;  // 1. 记录下一个
            int i = indexFor(e.hash, newTable.length);
            e.next = newTable[i];       // 2. 头插:新节点指向当前头
            newTable[i] = e;            // 3. 新节点成为头
            e = next;                   // 4. 处理下一个
        }
    }
}

死链原因: 多线程并发扩容时,头插法会反转链表顺序,两个线程同时 transfer 可能形成环形链表,导致 get() 时 CPU 100%(死循环)。

JDK 1.8 尾插法 — 解决死链:

java
// JDK 1.8 resize() 简化
// 使用高低位链表,保持原顺序,尾插法
Node<K,V> loHead = null, loTail = null;  // 低位链表(原位置)
Node<K,V> hiHead = null, hiTail = null;  // 高位链表(原位置 + 旧容量)

// 遍历每个节点
do {
    next = e.next;
    if ((e.hash & oldCap) == 0) {
        // 低位:保持在原位置
        if (loTail == null) loHead = e;
        else loTail.next = e;
        loTail = e;
    } else {
        // 高位:移动到 原位置 + oldCap
        if (hiTail == null) hiHead = e;
        else hiTail.next = e;
        hiTail = e;
    }
} while ((e = next) != null);

// 尾插法不会反转链表,避免了死链
// JDK 1.8 resize() 简化
// 使用高低位链表,保持原顺序,尾插法
Node<K,V> loHead = null, loTail = null;  // 低位链表(原位置)
Node<K,V> hiHead = null, hiTail = null;  // 高位链表(原位置 + 旧容量)

// 遍历每个节点
do {
    next = e.next;
    if ((e.hash & oldCap) == 0) {
        // 低位:保持在原位置
        if (loTail == null) loHead = e;
        else loTail.next = e;
        loTail = e;
    } else {
        // 高位:移动到 原位置 + oldCap
        if (hiTail == null) hiHead = e;
        else hiTail.next = e;
        hiTail = e;
    }
} while ((e = next) != null);

// 尾插法不会反转链表,避免了死链

扩容触发条件:

  • size > threshold(threshold = capacity × loadFactor,默认 16 × 0.75 = 12)
  • 链表长度 ≥ 8 且数组长度 < 64 时,优先扩容而不是树化

1.4.4 ConcurrentHashMap 线程安全原理 ​

JDK 1.7:分段锁(Segment)

java
// 默认 16 个 Segment,每个 Segment 独立加锁
// 并发度 = Segment 数量(默认 16)
// 不同 Segment 之间可以并发操作
// 默认 16 个 Segment,每个 Segment 独立加锁
// 并发度 = Segment 数量(默认 16)
// 不同 Segment 之间可以并发操作

JDK 1.8:CAS + synchronized

java
// put 操作简化
final V putVal(K key, V value, boolean onlyIfAbsent) {
    // 1. 如果数组为空,CAS 初始化(不锁定)
    if (tab == null) {
        // CAS 设置,只有一个线程成功
        if (U.compareAndSetInt(this, SIZECTL, sc, -1)) {
            // 初始化数组...
        }
    }
    // 2. 如果桶为空,CAS 直接插入(不锁定)
    else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
        if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value)))
            break;  // CAS 成功,无需加锁
    }
    // 3. 桶不为空,synchronized 锁住桶的头节点(细粒度锁)
    else {
        synchronized (f) {
            // 遍历链表或红黑树,插入或更新
        }
    }
}
// put 操作简化
final V putVal(K key, V value, boolean onlyIfAbsent) {
    // 1. 如果数组为空,CAS 初始化(不锁定)
    if (tab == null) {
        // CAS 设置,只有一个线程成功
        if (U.compareAndSetInt(this, SIZECTL, sc, -1)) {
            // 初始化数组...
        }
    }
    // 2. 如果桶为空,CAS 直接插入(不锁定)
    else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
        if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value)))
            break;  // CAS 成功,无需加锁
    }
    // 3. 桶不为空,synchronized 锁住桶的头节点(细粒度锁)
    else {
        synchronized (f) {
            // 遍历链表或红黑树,插入或更新
        }
    }
}

为什么 JDK 1.8 放弃分段锁?

  • 分段锁内存占用大(每个 Segment 都是独立的 ReentrantLock)
  • synchronized 在 JDK 1.6+ 经过大量优化(偏向锁、轻量锁、锁粗化),性能已接近 ReentrantLock
  • 细粒度锁到桶级别,并发度更高

get() 不需要加锁: Node 的 val 和 next 都用 volatile 修饰,保证可见性。

对比JDK 1.7 ConcurrentHashMapJDK 1.8 ConcurrentHashMap
锁粒度Segment 级别(默认 16 个)桶级别(每个 Node 头)
并发度最多 16理论上等于桶数量
实现ReentrantLockCAS + synchronized
链表转树不支持链表 ≥ 8 转红黑树

1.5 集合工具类 ​

Collections:提供了各种集合操作的静态方法

java
List<Integer> list = Arrays.asList(3, 1, 2);

// 排序
Collections.sort(list); // [1, 2, 3]

// 反转
Collections.reverse(list); // [3, 2, 1]

// 打乱顺序
Collections.shuffle(list);

// 获取最大值
Integer max = Collections.max(list);

// 获取最小值
Integer min = Collections.min(list);

// 填充
Collections.fill(list, 0); // [0, 0, 0]

// 线程安全集合
List<String> synchronizedList = Collections.synchronizedList(new ArrayList<>());
Set<String> synchronizedSet = Collections.synchronizedSet(new HashSet<>());
Map<String, Integer> synchronizedMap = Collections.synchronizedMap(new HashMap<>());
List<Integer> list = Arrays.asList(3, 1, 2);

// 排序
Collections.sort(list); // [1, 2, 3]

// 反转
Collections.reverse(list); // [3, 2, 1]

// 打乱顺序
Collections.shuffle(list);

// 获取最大值
Integer max = Collections.max(list);

// 获取最小值
Integer min = Collections.min(list);

// 填充
Collections.fill(list, 0); // [0, 0, 0]

// 线程安全集合
List<String> synchronizedList = Collections.synchronizedList(new ArrayList<>());
Set<String> synchronizedSet = Collections.synchronizedSet(new HashSet<>());
Map<String, Integer> synchronizedMap = Collections.synchronizedMap(new HashMap<>());

2. 泛型 ​

2.1 什么是泛型? ​

泛型允许在定义类、接口和方法时使用类型参数,提高代码的复用性和类型安全性。

2.2 泛型的好处 ​

  • 类型安全:在编译时检查类型,避免运行时类型转换异常
  • 代码复用:编写通用的代码,适用于多种数据类型
  • 可读性好:不需要类型转换,代码更简洁

2.3 泛型的使用 ​

2.3.1 泛型类 ​

java
// 定义泛型类
class Box<T> {
    private T content;
    
    public void setContent(T content) {
        this.content = content;
    }
    
    public T getContent() {
        return content;
    }
}

// 使用泛型类
Box<String> stringBox = new Box<>();
stringBox.setContent("Hello");
String str = stringBox.getContent();

Box<Integer> integerBox = new Box<>();
integerBox.setContent(100);
Integer num = integerBox.getContent();
// 定义泛型类
class Box<T> {
    private T content;
    
    public void setContent(T content) {
        this.content = content;
    }
    
    public T getContent() {
        return content;
    }
}

// 使用泛型类
Box<String> stringBox = new Box<>();
stringBox.setContent("Hello");
String str = stringBox.getContent();

Box<Integer> integerBox = new Box<>();
integerBox.setContent(100);
Integer num = integerBox.getContent();

2.3.2 泛型方法 ​

java
// 定义泛型方法
public <T> T getFirstElement(List<T> list) {
    if (list != null && !list.isEmpty()) {
        return list.get(0);
    }
    return null;
}

// 使用泛型方法
List<String> stringList = Arrays.asList("Java", "Python", "C++");
String firstStr = getFirstElement(stringList); // "Java"

List<Integer> intList = Arrays.asList(1, 2, 3);
Integer firstInt = getFirstElement(intList); // 1
// 定义泛型方法
public <T> T getFirstElement(List<T> list) {
    if (list != null && !list.isEmpty()) {
        return list.get(0);
    }
    return null;
}

// 使用泛型方法
List<String> stringList = Arrays.asList("Java", "Python", "C++");
String firstStr = getFirstElement(stringList); // "Java"

List<Integer> intList = Arrays.asList(1, 2, 3);
Integer firstInt = getFirstElement(intList); // 1

2.3.3 泛型接口 ​

java
// 定义泛型接口
interface Generator<T> {
    T generate();
}

// 实现泛型接口
class StringGenerator implements Generator<String> {
    @Override
    public String generate() {
        return "Generated string";
    }
}

class IntegerGenerator implements Generator<Integer> {
    @Override
    public Integer generate() {
        return new Random().nextInt(100);
    }
}
// 定义泛型接口
interface Generator<T> {
    T generate();
}

// 实现泛型接口
class StringGenerator implements Generator<String> {
    @Override
    public String generate() {
        return "Generated string";
    }
}

class IntegerGenerator implements Generator<Integer> {
    @Override
    public Integer generate() {
        return new Random().nextInt(100);
    }
}

2.3.4 泛型通配符 ​

  • ?:无限通配符
  • ? extends T:上限通配符,只能读取
  • ? super T:下限通配符,只能写入
java
// 无限通配符
public void printList(List<?> list) {
    for (Object obj : list) {
        System.out.println(obj);
    }
}

// 上限通配符(只能读取)
public double sumList(List<? extends Number> list) {
    double sum = 0;
    for (Number num : list) {
        sum += num.doubleValue();
    }
    return sum;
}

// 下限通配符(只能写入)
public void addNumbers(List<? super Integer> list) {
    list.add(1);
    list.add(2);
    list.add(3);
}
// 无限通配符
public void printList(List<?> list) {
    for (Object obj : list) {
        System.out.println(obj);
    }
}

// 上限通配符(只能读取)
public double sumList(List<? extends Number> list) {
    double sum = 0;
    for (Number num : list) {
        sum += num.doubleValue();
    }
    return sum;
}

// 下限通配符(只能写入)
public void addNumbers(List<? super Integer> list) {
    list.add(1);
    list.add(2);
    list.add(3);
}

3. 多线程 ​

3.1 什么是线程? ​

线程是程序执行的最小单位,一个进程可以包含多个线程。Java 是多线程语言,支持并发编程。

3.2 线程的创建方式 ​

3.2.1 继承 Thread 类 ​

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

// 使用
MyThread thread = new MyThread();
thread.start(); // 启动线程
class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("Thread running: " + Thread.currentThread().getName());
        // 线程执行的代码
    }
}

// 使用
MyThread thread = new MyThread();
thread.start(); // 启动线程

3.2.2 实现 Runnable 接口 ​

java
class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("Runnable running: " + Thread.currentThread().getName());
        // 线程执行的代码
    }
}

// 使用
Thread thread = new Thread(new MyRunnable());
thread.start();
class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("Runnable running: " + Thread.currentThread().getName());
        // 线程执行的代码
    }
}

// 使用
Thread thread = new Thread(new MyRunnable());
thread.start();

3.2.3 实现 Callable 接口 ​

java
class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        System.out.println("Callable running: " + Thread.currentThread().getName());
        return 42; // 返回结果
    }
}

// 使用
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Integer> future = executor.submit(new MyCallable());
Integer result = future.get(); // 获取结果,会阻塞
System.out.println("Result: " + result);
executor.shutdown();
class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        System.out.println("Callable running: " + Thread.currentThread().getName());
        return 42; // 返回结果
    }
}

// 使用
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Integer> future = executor.submit(new MyCallable());
Integer result = future.get(); // 获取结果,会阻塞
System.out.println("Result: " + result);
executor.shutdown();

3.2.4 使用线程池 ​

java
// 创建线程池
ExecutorService executor = Executors.newFixedThreadPool(5); // 固定大小线程池

// 提交任务
for (int i = 0; i < 10; i++) {
    final int taskId = i;
    executor.submit(() -> {
        System.out.println("Task " + taskId + " running on " + Thread.currentThread().getName());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return taskId;
    });
}

// 关闭线程池
executor.shutdown();
// 创建线程池
ExecutorService executor = Executors.newFixedThreadPool(5); // 固定大小线程池

// 提交任务
for (int i = 0; i < 10; i++) {
    final int taskId = i;
    executor.submit(() -> {
        System.out.println("Task " + taskId + " running on " + Thread.currentThread().getName());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return taskId;
    });
}

// 关闭线程池
executor.shutdown();

3.3 线程状态 ​

  • NEW:新建状态,尚未启动
  • RUNNABLE:可运行状态,正在执行或等待CPU资源
  • BLOCKED:阻塞状态,等待锁
  • WAITING:等待状态,等待其他线程的特定操作
  • TIMED_WAITING:超时等待状态,等待指定时间后自动唤醒
  • TERMINATED:终止状态,线程执行完毕

3.4 线程同步 ​

3.4.1 synchronized 关键字 ​

同步方法:

java
public synchronized void synchronizedMethod() {
    // 同步代码块,同一时间只有一个线程能执行
    System.out.println("Synchronized method: " + Thread.currentThread().getName());
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}
public synchronized void synchronizedMethod() {
    // 同步代码块,同一时间只有一个线程能执行
    System.out.println("Synchronized method: " + Thread.currentThread().getName());
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

同步代码块:

java
public void synchronizedBlock() {
    synchronized (this) { // 锁对象
        // 同步代码块
        System.out.println("Synchronized block: " + Thread.currentThread().getName());
    }
}
public void synchronizedBlock() {
    synchronized (this) { // 锁对象
        // 同步代码块
        System.out.println("Synchronized block: " + Thread.currentThread().getName());
    }
}

3.4.2 Lock 接口 ​

ReentrantLock:可重入锁,比 synchronized 更灵活

java
private final Lock lock = new ReentrantLock();

public void lockMethod() {
    lock.lock(); // 获取锁
    try {
        // 同步代码
        System.out.println("Lock method: " + Thread.currentThread().getName());
    } finally {
        lock.unlock(); // 释放锁,必须在 finally 中执行
    }
}
private final Lock lock = new ReentrantLock();

public void lockMethod() {
    lock.lock(); // 获取锁
    try {
        // 同步代码
        System.out.println("Lock method: " + Thread.currentThread().getName());
    } finally {
        lock.unlock(); // 释放锁,必须在 finally 中执行
    }
}

3.4.3 线程通信 ​

wait()、notify()、notifyAll():

java
class SharedResource {
    private int count = 0;
    private final Object lock = new Object();
    
    public void increment() throws InterruptedException {
        synchronized (lock) {
            while (count >= 5) {
                lock.wait(); // 等待,释放锁
            }
            count++;
            System.out.println("Increment: " + count + " by " + Thread.currentThread().getName());
            lock.notifyAll(); // 唤醒所有等待的线程
        }
    }
    
    public void decrement() throws InterruptedException {
        synchronized (lock) {
            while (count <= 0) {
                lock.wait(); // 等待,释放锁
            }
            count--;
            System.out.println("Decrement: " + count + " by " + Thread.currentThread().getName());
            lock.notifyAll(); // 唤醒所有等待的线程
        }
    }
}
class SharedResource {
    private int count = 0;
    private final Object lock = new Object();
    
    public void increment() throws InterruptedException {
        synchronized (lock) {
            while (count >= 5) {
                lock.wait(); // 等待,释放锁
            }
            count++;
            System.out.println("Increment: " + count + " by " + Thread.currentThread().getName());
            lock.notifyAll(); // 唤醒所有等待的线程
        }
    }
    
    public void decrement() throws InterruptedException {
        synchronized (lock) {
            while (count <= 0) {
                lock.wait(); // 等待,释放锁
            }
            count--;
            System.out.println("Decrement: " + count + " by " + Thread.currentThread().getName());
            lock.notifyAll(); // 唤醒所有等待的线程
        }
    }
}

3.5 线程池 ​

线程池的优势:

  • 减少线程创建和销毁的开销
  • 提高响应速度
  • 便于管理线程数量
  • 支持更多功能,如定时任务、批量执行等

常用线程池:

线程池类型描述
newFixedThreadPool(n)固定大小的线程池
newCachedThreadPool()可缓存的线程池,线程数可动态调整
newSingleThreadExecutor()单线程线程池
newScheduledThreadPool(n)支持定时和周期性任务的线程池
newWorkStealingPool()工作窃取线程池,适合大量耗时任务

自定义线程池:

java
ThreadPoolExecutor executor = new ThreadPoolExecutor(
    5, // 核心线程数
    10, // 最大线程数
    60L, // 非核心线程空闲时间
    TimeUnit.SECONDS, // 时间单位
    new LinkedBlockingQueue<>(100), // 工作队列
    Executors.defaultThreadFactory(), // 线程工厂
    new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
);
ThreadPoolExecutor executor = new ThreadPoolExecutor(
    5, // 核心线程数
    10, // 最大线程数
    60L, // 非核心线程空闲时间
    TimeUnit.SECONDS, // 时间单位
    new LinkedBlockingQueue<>(100), // 工作队列
    Executors.defaultThreadFactory(), // 线程工厂
    new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
);

4. IO 流 ​

4.1 IO 流的分类 ​

按流向分类:

  • 输入流:从数据源读取数据
  • 输出流:向目的地写入数据

按数据类型分类:

  • 字节流:以字节为单位,适合处理二进制数据
  • 字符流:以字符为单位,适合处理文本数据

4.2 字节流 ​

4.2.1 InputStream 和 OutputStream ​

InputStream:字节输入流的父类

java
// 文件输入流
try (FileInputStream fis = new FileInputStream("input.txt")) {
    byte[] buffer = new byte[1024];
    int length;
    while ((length = fis.read(buffer)) != -1) {
        // 处理读取的数据
        System.out.println(new String(buffer, 0, length));
    }
} catch (IOException e) {
    e.printStackTrace();
}
// 文件输入流
try (FileInputStream fis = new FileInputStream("input.txt")) {
    byte[] buffer = new byte[1024];
    int length;
    while ((length = fis.read(buffer)) != -1) {
        // 处理读取的数据
        System.out.println(new String(buffer, 0, length));
    }
} catch (IOException e) {
    e.printStackTrace();
}

OutputStream:字节输出流的父类

java
// 文件输出流
try (FileOutputStream fos = new FileOutputStream("output.txt")) {
    String content = "Hello, Java IO!";
    fos.write(content.getBytes());
} catch (IOException e) {
    e.printStackTrace();
}
// 文件输出流
try (FileOutputStream fos = new FileOutputStream("output.txt")) {
    String content = "Hello, Java IO!";
    fos.write(content.getBytes());
} catch (IOException e) {
    e.printStackTrace();
}

4.2.2 缓冲流 ​

BufferedInputStream 和 BufferedOutputStream:提供缓冲功能,提高 IO 效率

java
// 缓冲输入流
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("input.txt"));
     BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("output.txt"))) {
    
    byte[] buffer = new byte[1024];
    int length;
    while ((length = bis.read(buffer)) != -1) {
        bos.write(buffer, 0, length);
    }
    
} catch (IOException e) {
    e.printStackTrace();
}
// 缓冲输入流
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("input.txt"));
     BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("output.txt"))) {
    
    byte[] buffer = new byte[1024];
    int length;
    while ((length = bis.read(buffer)) != -1) {
        bos.write(buffer, 0, length);
    }
    
} catch (IOException e) {
    e.printStackTrace();
}

4.3 字符流 ​

4.3.1 Reader 和 Writer ​

Reader:字符输入流的父类

java
// 文件输入流
try (FileReader fr = new FileReader("input.txt")) {
    char[] buffer = new char[1024];
    int length;
    while ((length = fr.read(buffer)) != -1) {
        System.out.println(new String(buffer, 0, length));
    }
} catch (IOException e) {
    e.printStackTrace();
}
// 文件输入流
try (FileReader fr = new FileReader("input.txt")) {
    char[] buffer = new char[1024];
    int length;
    while ((length = fr.read(buffer)) != -1) {
        System.out.println(new String(buffer, 0, length));
    }
} catch (IOException e) {
    e.printStackTrace();
}

Writer:字符输出流的父类

java
// 文件输出流
try (FileWriter fw = new FileWriter("output.txt")) {
    String content = "Hello, Java IO!";
    fw.write(content);
} catch (IOException e) {
    e.printStackTrace();
}
// 文件输出流
try (FileWriter fw = new FileWriter("output.txt")) {
    String content = "Hello, Java IO!";
    fw.write(content);
} catch (IOException e) {
    e.printStackTrace();
}

4.3.2 缓冲字符流 ​

BufferedReader 和 BufferedWriter:提供缓冲功能,支持按行读取

java
// 缓冲字符流
try (BufferedReader br = new BufferedReader(new FileReader("input.txt"));
     BufferedWriter bw = new BufferedWriter(new FileWriter("output.txt"))) {
    
    String line;
    while ((line = br.readLine()) != null) {
        System.out.println(line);
        bw.write(line);
        bw.newLine(); // 换行
    }
    
} catch (IOException e) {
    e.printStackTrace();
}
// 缓冲字符流
try (BufferedReader br = new BufferedReader(new FileReader("input.txt"));
     BufferedWriter bw = new BufferedWriter(new FileWriter("output.txt"))) {
    
    String line;
    while ((line = br.readLine()) != null) {
        System.out.println(line);
        bw.write(line);
        bw.newLine(); // 换行
    }
    
} catch (IOException e) {
    e.printStackTrace();
}

4.4 转换流 ​

InputStreamReader 和 OutputStreamWriter:用于字节流和字符流之间的转换

java
// 转换流,指定字符编码
try (InputStreamReader isr = new InputStreamReader(new FileInputStream("input.txt"), StandardCharsets.UTF_8);
     OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("output.txt"), StandardCharsets.UTF_8)) {
    
    char[] buffer = new char[1024];
    int length;
    while ((length = isr.read(buffer)) != -1) {
        osw.write(buffer, 0, length);
    }
    
} catch (IOException e) {
    e.printStackTrace();
}
// 转换流,指定字符编码
try (InputStreamReader isr = new InputStreamReader(new FileInputStream("input.txt"), StandardCharsets.UTF_8);
     OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("output.txt"), StandardCharsets.UTF_8)) {
    
    char[] buffer = new char[1024];
    int length;
    while ((length = isr.read(buffer)) != -1) {
        osw.write(buffer, 0, length);
    }
    
} catch (IOException e) {
    e.printStackTrace();
}

4.5 对象流 ​

ObjectInputStream 和 ObjectOutputStream:用于对象的序列化和反序列化

序列化:将对象转换为字节序列 反序列化:将字节序列转换为对象

java
// 序列化对象
class Person implements Serializable {
    private static final long serialVersionUID = 1L; // 序列化版本号
    private String name;
    private int age;
    // 构造函数、getter、setter
}

// 序列化
Person person = new Person("Java", 25);
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.ser"))) {
    oos.writeObject(person);
    System.out.println("对象已序列化");
} catch (IOException e) {
    e.printStackTrace();
}

// 反序列化
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.ser"))) {
    Person deserializedPerson = (Person) ois.readObject();
    System.out.println("对象已反序列化:" + deserializedPerson.getName());
} catch (IOException | ClassNotFoundException e) {
    e.printStackTrace();
}
// 序列化对象
class Person implements Serializable {
    private static final long serialVersionUID = 1L; // 序列化版本号
    private String name;
    private int age;
    // 构造函数、getter、setter
}

// 序列化
Person person = new Person("Java", 25);
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.ser"))) {
    oos.writeObject(person);
    System.out.println("对象已序列化");
} catch (IOException e) {
    e.printStackTrace();
}

// 反序列化
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.ser"))) {
    Person deserializedPerson = (Person) ois.readObject();
    System.out.println("对象已反序列化:" + deserializedPerson.getName());
} catch (IOException | ClassNotFoundException e) {
    e.printStackTrace();
}

4.6 NIO ​

Java NIO(New IO)是 Java 1.4 引入的新 IO API,提供了非阻塞 IO 操作。

4.6.1 核心组件 ​

  • Buffer:缓冲区,用于存储数据
  • Channel:通道,用于数据传输
  • Selector:选择器,用于监听多个通道的事件

4.6.2 NIO 示例 ​

java
// NIO 读取文件
Path path = Paths.get("input.txt");
try (SeekableByteChannel channel = Files.newByteChannel(path)) {
    ByteBuffer buffer = ByteBuffer.allocate(1024);
    while (channel.read(buffer) != -1) {
        buffer.flip(); // 切换到读模式
        while (buffer.hasRemaining()) {
            System.out.print((char) buffer.get());
        }
        buffer.clear(); // 切换到写模式
    }
} catch (IOException e) {
    e.printStackTrace();
}

// NIO 写入文件
Path outputPath = Paths.get("output.txt");
String content = "Hello, Java NIO!";
try {
    Files.write(outputPath, content.getBytes(), StandardOpenOption.CREATE);
} catch (IOException e) {
    e.printStackTrace();
}

// NIO 复制文件
Path source = Paths.get("input.txt");
Path target = Paths.get("output.txt");
try {
    Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
    e.printStackTrace();
}
// NIO 读取文件
Path path = Paths.get("input.txt");
try (SeekableByteChannel channel = Files.newByteChannel(path)) {
    ByteBuffer buffer = ByteBuffer.allocate(1024);
    while (channel.read(buffer) != -1) {
        buffer.flip(); // 切换到读模式
        while (buffer.hasRemaining()) {
            System.out.print((char) buffer.get());
        }
        buffer.clear(); // 切换到写模式
    }
} catch (IOException e) {
    e.printStackTrace();
}

// NIO 写入文件
Path outputPath = Paths.get("output.txt");
String content = "Hello, Java NIO!";
try {
    Files.write(outputPath, content.getBytes(), StandardOpenOption.CREATE);
} catch (IOException e) {
    e.printStackTrace();
}

// NIO 复制文件
Path source = Paths.get("input.txt");
Path target = Paths.get("output.txt");
try {
    Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
    e.printStackTrace();
}

5. 反射机制 ​

5.1 什么是反射? ​

反射是 Java 程序在运行时获取类的信息、创建对象、调用方法和访问属性的能力。

5.2 反射的作用 ​

  • 在运行时获取类的信息
  • 动态创建对象
  • 动态调用方法
  • 动态访问和修改属性
  • 实现框架和工具类

5.3 反射的核心类 ​

  • Class:表示类的信息
  • Constructor:表示类的构造方法
  • Method:表示类的方法
  • Field:表示类的属性
  • Annotation:表示类的注解

5.4 反射示例 ​

5.4.1 获取 Class 对象 ​

java
// 方式1:使用 Class.forName()
Class<?> clazz1 = Class.forName("java.lang.String");

// 方式2:使用 .class
Class<?> clazz2 = String.class;

// 方式3:使用对象的 getClass() 方法
Class<?> clazz3 = "Hello".getClass();
// 方式1:使用 Class.forName()
Class<?> clazz1 = Class.forName("java.lang.String");

// 方式2:使用 .class
Class<?> clazz2 = String.class;

// 方式3:使用对象的 getClass() 方法
Class<?> clazz3 = "Hello".getClass();

5.4.2 动态创建对象 ​

java
// 使用无参构造方法
Class<?> clazz = Person.class;
Person person1 = (Person) clazz.getDeclaredConstructor().newInstance();

// 使用有参构造方法
Constructor<?> constructor = clazz.getDeclaredConstructor(String.class, int.class);
Person person2 = (Person) constructor.newInstance("Java", 25);
// 使用无参构造方法
Class<?> clazz = Person.class;
Person person1 = (Person) clazz.getDeclaredConstructor().newInstance();

// 使用有参构造方法
Constructor<?> constructor = clazz.getDeclaredConstructor(String.class, int.class);
Person person2 = (Person) constructor.newInstance("Java", 25);

5.4.3 动态调用方法 ​

java
Class<?> clazz = Person.class;
Person person = (Person) clazz.getDeclaredConstructor().newInstance();

// 获取方法
Method setNameMethod = clazz.getMethod("setName", String.class);
Method getNameMethod = clazz.getMethod("getName");

// 调用方法
setNameMethod.invoke(person, "Java");
String name = (String) getNameMethod.invoke(person);
System.out.println("Name: " + name); // "Java"

// 调用私有方法
Method privateMethod = clazz.getDeclaredMethod("privateMethod");
privateMethod.setAccessible(true); // 设置可访问
privateMethod.invoke(person);
Class<?> clazz = Person.class;
Person person = (Person) clazz.getDeclaredConstructor().newInstance();

// 获取方法
Method setNameMethod = clazz.getMethod("setName", String.class);
Method getNameMethod = clazz.getMethod("getName");

// 调用方法
setNameMethod.invoke(person, "Java");
String name = (String) getNameMethod.invoke(person);
System.out.println("Name: " + name); // "Java"

// 调用私有方法
Method privateMethod = clazz.getDeclaredMethod("privateMethod");
privateMethod.setAccessible(true); // 设置可访问
privateMethod.invoke(person);

5.4.4 动态访问和修改属性 ​

java
Class<?> clazz = Person.class;
Person person = (Person) clazz.getDeclaredConstructor().newInstance();

// 获取属性
Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true); // 设置可访问私有属性

// 修改属性
nameField.set(person, "Java");

// 获取属性值
String name = (String) nameField.get(person);
System.out.println("Name: " + name); // "Java"
Class<?> clazz = Person.class;
Person person = (Person) clazz.getDeclaredConstructor().newInstance();

// 获取属性
Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true); // 设置可访问私有属性

// 修改属性
nameField.set(person, "Java");

// 获取属性值
String name = (String) nameField.get(person);
System.out.println("Name: " + name); // "Java"

5.4.5 获取类的信息 ​

java
Class<?> clazz = Person.class;

// 获取类名
System.out.println("类名:" + clazz.getName());
System.out.println("简单类名:" + clazz.getSimpleName());
System.out.println("包名:" + clazz.getPackage().getName());

// 获取父类
Class<?> superClass = clazz.getSuperclass();
System.out.println("父类:" + superClass.getName());

// 获取接口
Class<?>[] interfaces = clazz.getInterfaces();
for (Class<?> iface : interfaces) {
    System.out.println("接口:" + iface.getName());
}

// 获取构造方法
Constructor<?>[] constructors = clazz.getDeclaredConstructors();
for (Constructor<?> constructor : constructors) {
    System.out.println("构造方法:" + constructor.getName());
}

// 获取方法
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
    System.out.println("方法:" + method.getName());
}

// 获取属性
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
    System.out.println("属性:" + field.getName());
}
Class<?> clazz = Person.class;

// 获取类名
System.out.println("类名:" + clazz.getName());
System.out.println("简单类名:" + clazz.getSimpleName());
System.out.println("包名:" + clazz.getPackage().getName());

// 获取父类
Class<?> superClass = clazz.getSuperclass();
System.out.println("父类:" + superClass.getName());

// 获取接口
Class<?>[] interfaces = clazz.getInterfaces();
for (Class<?> iface : interfaces) {
    System.out.println("接口:" + iface.getName());
}

// 获取构造方法
Constructor<?>[] constructors = clazz.getDeclaredConstructors();
for (Constructor<?> constructor : constructors) {
    System.out.println("构造方法:" + constructor.getName());
}

// 获取方法
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
    System.out.println("方法:" + method.getName());
}

// 获取属性
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
    System.out.println("属性:" + field.getName());
}

6. 注解 ​

6.1 什么是注解? ​

注解(Annotation)是 Java 5 引入的一种元数据,用于为代码提供额外的信息。

6.2 注解的分类 ​

内置注解:

  • @Override:标记方法重写父类方法
  • @Deprecated:标记已过时的元素
  • @SuppressWarnings:抑制编译器警告
  • @FunctionalInterface:标记函数式接口
  • @SafeVarargs:标记安全的可变参数方法

元注解:用于定义其他注解的注解

  • @Target:指定注解可以应用的目标元素
  • @Retention:指定注解的保留策略
  • @Documented:标记注解将被包含在 Javadoc 中
  • @Inherited:标记注解可以被继承
  • @Repeatable:标记注解可以重复应用

6.3 自定义注解 ​

java
// 定义注解
@Target(ElementType.METHOD) // 注解可以应用于方法
@Retention(RetentionPolicy.RUNTIME) // 注解在运行时保留
@Documented // 注解将被包含在 Javadoc 中
public @interface MyAnnotation {
    // 注解属性
    String value() default ""; // 默认值
    int count() default 1;
    String[] names() default {};
}

// 使用注解
public class MyClass {
    @MyAnnotation(value = "Hello", count = 5, names = {"Java", "Python"})
    public void myMethod() {
        // 方法内容
    }
}

// 反射获取注解信息
Class<?> clazz = MyClass.class;
Method method = clazz.getMethod("myMethod");
if (method.isAnnotationPresent(MyAnnotation.class)) {
    MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
    System.out.println("Value: " + annotation.value()); // "Hello"
    System.out.println("Count: " + annotation.count()); // 5
    System.out.println("Names: " + Arrays.toString(annotation.names())); // ["Java", "Python"]
}
// 定义注解
@Target(ElementType.METHOD) // 注解可以应用于方法
@Retention(RetentionPolicy.RUNTIME) // 注解在运行时保留
@Documented // 注解将被包含在 Javadoc 中
public @interface MyAnnotation {
    // 注解属性
    String value() default ""; // 默认值
    int count() default 1;
    String[] names() default {};
}

// 使用注解
public class MyClass {
    @MyAnnotation(value = "Hello", count = 5, names = {"Java", "Python"})
    public void myMethod() {
        // 方法内容
    }
}

// 反射获取注解信息
Class<?> clazz = MyClass.class;
Method method = clazz.getMethod("myMethod");
if (method.isAnnotationPresent(MyAnnotation.class)) {
    MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
    System.out.println("Value: " + annotation.value()); // "Hello"
    System.out.println("Count: " + annotation.count()); // 5
    System.out.println("Names: " + Arrays.toString(annotation.names())); // ["Java", "Python"]
}

7. 总结 ​

Java 核心特性是 Java 编程的重要组成部分,包括:

  1. 集合框架:提供了各种数据结构,如 List、Set、Map 等
  2. 泛型:提高代码的复用性和类型安全性
  3. 多线程:支持并发编程,提高程序的执行效率
  4. IO 流:用于处理文件和网络数据
  5. 反射机制:允许在运行时获取和操作类的信息
  6. 注解:提供元数据支持,用于框架和工具类

掌握这些核心特性,将有助于编写高效、可维护的 Java 代码。在实际开发中,这些特性经常被结合使用,例如在 Spring 框架中,大量使用了反射和注解。