Java 核心特性笔记
1. 集合框架
1.1 什么是集合框架?
Java 集合框架是用于存储和操作数据的一组接口和类,位于 java.util 包中。
1.2 集合框架体系
Collection接口:存储单个元素的集合List:有序、可重复Set:无序、不可重复Queue:队列,先进先出
Map接口:存储键值对的集合
1.3 常用集合类
1.3.1 List 接口
ArrayList:基于动态数组实现,查询快,增删慢
// 创建 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:基于双向链表实现,查询慢,增删快
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:基于哈希表实现,无序,不允许重复
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:基于哈希表+链表实现,保持插入顺序
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:基于红黑树实现,有序(自然排序或自定义排序)
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, 1Set<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, 11.3.3 Map 接口
HashMap:基于哈希表实现,无序,键不允许重复
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:基于哈希表+链表实现,保持插入顺序
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:基于红黑树实现,键有序
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,适合多线程环境
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 结构:
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 的幂?
核心原因:用位运算代替取模,提高效率。
// 计算 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 头插法 — 存在死链问题:
// 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 尾插法 — 解决死链:
// 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)
// 默认 16 个 Segment,每个 Segment 独立加锁
// 并发度 = Segment 数量(默认 16)
// 不同 Segment 之间可以并发操作// 默认 16 个 Segment,每个 Segment 独立加锁
// 并发度 = Segment 数量(默认 16)
// 不同 Segment 之间可以并发操作JDK 1.8:CAS + synchronized
// 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 ConcurrentHashMap | JDK 1.8 ConcurrentHashMap |
|---|---|---|
| 锁粒度 | Segment 级别(默认 16 个) | 桶级别(每个 Node 头) |
| 并发度 | 最多 16 | 理论上等于桶数量 |
| 实现 | ReentrantLock | CAS + synchronized |
| 链表转树 | 不支持 | 链表 ≥ 8 转红黑树 |
1.5 集合工具类
Collections:提供了各种集合操作的静态方法
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 泛型类
// 定义泛型类
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 泛型方法
// 定义泛型方法
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); // 12.3.3 泛型接口
// 定义泛型接口
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:下限通配符,只能写入
// 无限通配符
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 类
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 接口
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 接口
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 使用线程池
// 创建线程池
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 关键字
同步方法:
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();
}
}同步代码块:
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 更灵活
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():
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() | 工作窃取线程池,适合大量耗时任务 |
自定义线程池:
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:字节输入流的父类
// 文件输入流
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:字节输出流的父类
// 文件输出流
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 效率
// 缓冲输入流
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:字符输入流的父类
// 文件输入流
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:字符输出流的父类
// 文件输出流
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:提供缓冲功能,支持按行读取
// 缓冲字符流
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:用于字节流和字符流之间的转换
// 转换流,指定字符编码
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:用于对象的序列化和反序列化
序列化:将对象转换为字节序列 反序列化:将字节序列转换为对象
// 序列化对象
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 示例
// 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 对象
// 方式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 动态创建对象
// 使用无参构造方法
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 动态调用方法
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 动态访问和修改属性
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 获取类的信息
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 自定义注解
// 定义注解
@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 编程的重要组成部分,包括:
- 集合框架:提供了各种数据结构,如 List、Set、Map 等
- 泛型:提高代码的复用性和类型安全性
- 多线程:支持并发编程,提高程序的执行效率
- IO 流:用于处理文件和网络数据
- 反射机制:允许在运行时获取和操作类的信息
- 注解:提供元数据支持,用于框架和工具类
掌握这些核心特性,将有助于编写高效、可维护的 Java 代码。在实际开发中,这些特性经常被结合使用,例如在 Spring 框架中,大量使用了反射和注解。
Cao Note