Java第二课:集合框架、异常处理、多线程编程
一、集合框架
1. 概述
Java集合框架提供了一组用于存储和操作对象的接口和类,旨在简化数据结构的使用和提高代码的可维护性。集合框架主要包括以下几个核心接口:
- Collection:集合框架的根接口,定义了基本的集合操作。List、Set和Queue等接口都是Collection的子接口。
- List:有序集合,允许重复元素。常见实现类包括
ArrayList和LinkedList。ArrayList:基于动态数组实现,适合随机访问和遍历。LinkedList:基于链表实现,适合频繁的插入和删除操作。
- Set:不允许重复元素的集合。常见实现类包括
HashSet和TreeSet。HashSet:基于哈希表实现,不保证元素的顺序。TreeSet:基于红黑树实现,元素按自然顺序或自定义顺序排序。
- Queue:先进先出(FIFO)的集合,适用于队列操作。常见实现类有
LinkedList和ArrayDeque。 - Map:用于存储键值对的集合,不属于
Collection接口的子接口。常见实现类包括HashMap和TreeMap。HashMap:基于哈希表实现,允许一个null键和多个null值。TreeMap:基于红黑树实现,键按照自然顺序或自定义顺序排序。
2. 使用示例
以下示例展示了如何使用 ArrayList 创建一个可调整大小的数组,并添加和打印元素:
import java.util.ArrayList;
class Main {
public static void main(String[] args) {
ArrayList animals = new ArrayList<>();
animals.add("Dog");
animals.add("Cat");
animals.add("Horse");
System.out.println("ArrayList: " + animals);
}
}
输出结果:
ArrayList: [Dog, Cat, Horse]
3. 迭代器的使用
迭代器提供了一种遍历集合元素的方式,而无需直接访问集合的内部结构。以下是使用迭代器遍历 ArrayList 的示例:
import java.util.ArrayList;
import java.util.Iterator;
public class IteratorExample {
public static void main(String[] args) {
ArrayList fruits = new ArrayList<>();
fruits.add("Apple");
fruits.add("Banana");
fruits.add("Cherry");
Iterator iterator = fruits.iterator();
while(iterator.hasNext()) {
String fruit = iterator.next();
System.out.println(fruit);
}
}
}
4. Collections工具类
Collections 类提供了一些静态方法,用于对集合进行排序、查找、反转等操作。以下是一些常用方法的示例:
import java.util.ArrayList;
import java.util.Collections;
public class CollectionsExample {
public static void main(String[] args) {
ArrayList numbers = new ArrayList<>();
numbers.add(5);
numbers.add(2);
numbers.add(9);
numbers.add(1);
// 排序
Collections.sort(numbers);
System.out.println("Sorted List: " + numbers);
// 反转
Collections.reverse(numbers);
System.out.println("Reversed List: " + numbers);
// 查找
int max = Collections.max(numbers);
System.out.println("Max Number: " + max);
}
}
输出结果:
Sorted List: [1, 2, 5, 9]
Reversed List: [9, 5, 2, 1]
Max Number: 9
二、异常处理
1. 异常处理机制
Java通过五个关键字来实现异常处理:try、catch、throw、throws 和 finally。
- try:包含可能会抛出异常的代码块。
- catch:用于捕获和处理
try块中抛出的异常。 - finally:无论是否发生异常,都会执行的代码块,常用于释放资源。
- throw:用于手动抛出一个异常。
- throws:用于在方法签名中声明该方法可能会抛出的异常。
2. 分类
- 受检异常(Checked Exceptions):必须在代码中显式处理的异常,例如
IOException和SQLException。 - 非受检异常(Unchecked Exceptions):运行时异常,无需强制处理,例如
NullPointerException和ArrayIndexOutOfBoundsException。 - 错误(Errors):严重问题,如
OutOfMemoryError,通常不建议程序中捕获。
3. 使用示例
以下示例展示了如何使用 try-catch-finally 块来处理异常:
public class ExceptionHandlingExample {
public static void main(String[] args) {
try {
int result = divide(10, 0);
System.out.println("Result: " + result);
} catch(ArithmeticException e) {
System.out.println("捕获到异常: " + e.getMessage());
} finally {
System.out.println("无论是否发生异常都会执行这里的代码。");
}
}
public static int divide(int a, int b) {
return a / b;
}
}
输出结果:
捕获到异常: / by zero
无论是否发生异常都会执行这里的代码。
4. 自定义异常
有时,内置的异常类型无法满足特定需求,此时可以创建自定义异常类:
class InsufficientFundsException extends Exception {
public InsufficientFundsException(String message) {
super(message);
}
}
public class CustomExceptionExample {
public static void main(String[] args) {
try {
withdraw(100);
} catch(InsufficientFundsException e) {
System.out.println("异常: " + e.getMessage());
}
}
public static void withdraw(int amount) throws InsufficientFundsException {
int balance = 50;
if(amount > balance) {
throw new InsufficientFundsException("余额不足,无法完成取款。");
}
System.out.println("成功取出: " + amount);
}
}
输出结果:
异常: 余额不足,无法完成取款。
三、多线程编程
1. 创建线程的方式
Java提供了两种主要方式来创建线程:
- 继承
Thread类public class MyThread extends Thread { @Override public void run() { System.out.println("这是一个线程。"); } public static void main(String[] args) { MyThread thread = new MyThread(); thread.start(); } } - 实现
Runnable接口public class RunnableExample implements Runnable { @Override public void run() { System.out.println("这是一个线程。"); } public static void main(String[] args) { Thread thread = new Thread(new RunnableExample()); thread.start(); } }
2. 线程生命周期
线程在生命周期中会经历以下几个状态:
- 新建(New):线程对象被创建,但尚未启动。
- 就绪(Runnable):线程已启动,正在等待CPU调度。
- 运行(Running):线程正在执行任务。
- 阻塞(Blocked/Waiting):线程由于某种原因暂时无法执行,如等待资源或等待另一个线程完成。
- 死亡(Terminated):线程已完成执行或因异常终止。
3. 线程同步机制
在多线程环境中,多个线程可能会同时访问共享资源,导致数据不一致的问题。Java提供了多种同步机制来解决这一问题:
synchronized关键字用于方法或代码块,确保同一时间只有一个线程可以访问被同步的资源。
public class SynchronizedExample { private int count = 0; public synchronized void increment() { count++; } public static void main(String[] args) throws InterruptedException { SynchronizedExample example = new SynchronizedExample(); Thread t1 = new Thread(() -> { for(int i=0; i<1000; i++) { example.increment(); } }); Thread t2 = new Thread(() -> { for(int i=0; i<1000; i++) { example.increment(); } }); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println("最终计数值: " + example.count); } }ReentrantLock类提供了比
synchronized更灵活的锁机制,可以实现更复杂的同步控制。import java.util.concurrent.locks.ReentrantLock; public class ReentrantLockExample { private int count = 0; private ReentrantLock lock = new ReentrantLock(); public void increment() { lock.lock(); try { count++; } finally { lock.unlock(); } } public static void main(String[] args) throws InterruptedException { ReentrantLockExample example = new ReentrantLockExample(); Thread t1 = new Thread(() -> { for(int i=0; i<1000; i++) { example.increment(); } }); Thread t2 = new Thread(() -> { for(int i=0; i<1000; i++) { example.increment(); } }); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println("最终计数值: " + example.count); } }
4. 线程间通信
线程间通信用于协调多个线程的执行顺序,确保数据的一致性。Java提供了多种方法和工具:
-
wait()和notify()方法用于在同步代码块中实现线程间的等待和通知机制。
public class WaitNotifyExample { private final Object lock = new Object(); private boolean isResourceAvailable = false; public void producer() { synchronized(lock) { try { // 生产资源 isResourceAvailable = true; lock.notify(); } catch(Exception e) { e.printStackTrace(); } } } public void consumer() { synchronized(lock) { while(!isResourceAvailable) { try { lock.wait(); } catch(InterruptedException e) { e.printStackTrace(); } } // 消费资源 isResourceAvailable = false; } } } -
并发工具类
Java在
java.util.concurrent包中提供了许多高级的并发工具,如CountDownLatch和Semaphore等,用于实现复杂的线程协调。import java.util.concurrent.CountDownLatch; public class CountDownLatchExample { public static void main(String[] args) throws InterruptedException { int numberOfThreads = 3; CountDownLatch latch = new CountDownLatch(numberOfThreads); for(int i=0; i { System.out.println(Thread.currentThread().getName() + " 完成任务。"); latch.countDown(); }).start(); } latch.await(); System.out.println("所有线程已完成,主线程继续执行。"); } }
5. 并发编程的高级工具
除了上述工具外,Java还提供了 Executor 框架,用于管理线程池和任务调度。此外, ForkJoinPool 适用于分治算法,可以有效利用多核处理器的性能。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ExecutorExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(5);
for(int i=0; i<10; i++) {
int task = i;
executor.execute(() -> {
System.out.println("执行任务 " + task + " 由 " + Thread.currentThread().getName());
});
}
executor.shutdown();
}
}
输出示例:
执行任务 0 由 pool-1-thread-1
执行任务 1 由 pool-1-thread-2
...
执行任务 9 由 pool-1-thread-5
学习建议
建议参考以下资源进行深入学习和实践:
实践是掌握编程技能的关键,多编写代码,解决实际问题,将有助于更好地理解和应用这些概念。理解每个概念背后的原理比单纯记住语法更为重要。
Last updated January 3, 2025