Ask me anything
Ithy Logo
Sign in to save

Java第二课:集合框架、异常处理、多线程编程

Java Scheduled Task Execution with ScheduledExecutorService

一、集合框架

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

AI-assisted research. Sources, methodology and corrections · Source-checked guides and tools · Explore published answers

Ask Ithy AI