Java学习第4天 - 异常处理与集合框架

学习时间: 4-5小时
学习目标: 掌握Java异常处理机制和集合框架的使用


📋 详细学习清单

✅ 第一部分:异常处理(Exception Handling)(90分钟)

1. 异常概念与JavaScript对比

JavaScript (你熟悉的错误处理)

javascript 复制代码
// JavaScript 错误处理
function divide(a, b) {
    try {
        if (b === 0) {
            throw new Error("除数不能为0");
        }
        return a / b;
    } catch (error) {
        console.error("发生错误:", error.message);
        return null;
    } finally {
        console.log("计算结束");
    }
}

// 使用Promise的错误处理
async function fetchData() {
    try {
        const response = await fetch('/api/data');
        const data = await response.json();
        return data;
    } catch (error) {
        console.error("请求失败:", error);
        throw error;
    }
}

Java (今天学习的异常处理)

java 复制代码
// Java 异常处理
public class ExceptionExample {
    
    // 方法声明抛出异常
    public static double divide(int a, int b) throws ArithmeticException {
        try {
            if (b == 0) {
                throw new ArithmeticException("除数不能为0");
            }
            return (double) a / b;
        } catch (ArithmeticException e) {
            System.err.println("发生算术异常: " + e.getMessage());
            throw e; // 重新抛出异常
        } finally {
            System.out.println("计算方法执行完毕");
        }
    }
    
    public static void main(String[] args) {
        try {
            double result1 = divide(10, 2);
            System.out.println("10 ÷ 2 = " + result1);
            
            double result2 = divide(10, 0); // 这里会抛出异常
            System.out.println("10 ÷ 0 = " + result2);
            
        } catch (ArithmeticException e) {
            System.out.println("主方法捕获异常: " + e.getMessage());
        }
    }
}

2. Java异常体系结构

java 复制代码
/**
 * Java异常层次结构:
 * 
 * Throwable (所有异常的父类)
 * ├── Error (系统级错误,不建议捕获)
 * │   ├── OutOfMemoryError
 * │   └── StackOverflowError
 * └── Exception (程序异常,需要处理)
 *     ├── RuntimeException (运行时异常,unchecked)
 *     │   ├── NullPointerException
 *     │   ├── ArrayIndexOutOfBoundsException
 *     │   ├── IllegalArgumentException
 *     │   └── ArithmeticException
 *     └── CheckedException (编译时异常,checked)
 *         ├── IOException
 *         ├── ClassNotFoundException
 *         └── SQLException
 */

public class ExceptionHierarchy {
    
    // 演示不同类型的异常
    public static void demonstrateExceptions() {
        
        // 1. NullPointerException (运行时异常)
        try {
            String str = null;
            int length = str.length(); // 会抛出NullPointerException
        } catch (NullPointerException e) {
            System.out.println("空指针异常: " + e.getMessage());
        }
        
        // 2. ArrayIndexOutOfBoundsException (运行时异常)
        try {
            int[] arr = {1, 2, 3};
            int value = arr[5]; // 会抛出数组越界异常
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("数组越界异常: " + e.getMessage());
        }
        
        // 3. NumberFormatException (运行时异常)
        try {
            String numStr = "abc123";
            int num = Integer.parseInt(numStr); // 会抛出数字格式异常
        } catch (NumberFormatException e) {
            System.out.println("数字格式异常: " + e.getMessage());
        }
        
        // 4. 多重catch
        try {
            String[] array = {"10", "20", null, "abc"};
            for (String s : array) {
                int num = Integer.parseInt(s);
                System.out.println("转换结果: " + num);
            }
        } catch (NullPointerException e) {
            System.out.println("遇到空值: " + e.getMessage());
        } catch (NumberFormatException e) {
            System.out.println("数字转换失败: " + e.getMessage());
        } catch (Exception e) {
            System.out.println("其他异常: " + e.getMessage());
        }
    }
}

3. 自定义异常类

java 复制代码
// 自定义异常类
class CustomBusinessException extends Exception {
    private int errorCode;
    
    public CustomBusinessException(String message) {
        super(message);
    }
    
    public CustomBusinessException(String message, int errorCode) {
        super(message);
        this.errorCode = errorCode;
    }
    
    public CustomBusinessException(String message, Throwable cause) {
        super(message, cause);
    }
    
    public int getErrorCode() {
        return errorCode;
    }
}

// 银行账户示例
class BankAccount {
    private String accountId;
    private double balance;
    
    public BankAccount(String accountId, double initialBalance) {
        this.accountId = accountId;
        this.balance = initialBalance;
    }
    
    // 取款方法,演示抛出自定义异常
    public void withdraw(double amount) throws CustomBusinessException {
        if (amount <= 0) {
            throw new CustomBusinessException("取款金额必须大于0", 1001);
        }
        
        if (amount > balance) {
            throw new CustomBusinessException(
                String.format("余额不足,当前余额: %.2f, 取款金额: %.2f", balance, amount), 
                1002
            );
        }
        
        balance -= amount;
        System.out.printf("取款成功,取款金额: %.2f, 剩余余额: %.2f%n", amount, balance);
    }
    
    public double getBalance() {
        return balance;
    }
}

// 使用自定义异常
public class CustomExceptionDemo {
    public static void main(String[] args) {
        BankAccount account = new BankAccount("ACC001", 1000.0);
        
        try {
            account.withdraw(500.0);  // 正常取款
            account.withdraw(-100.0); // 无效金额
        } catch (CustomBusinessException e) {
            System.err.println("业务异常 [错误码: " + e.getErrorCode() + "] " + e.getMessage());
        }
        
        try {
            account.withdraw(800.0);  // 余额不足
        } catch (CustomBusinessException e) {
            System.err.println("业务异常 [错误码: " + e.getErrorCode() + "] " + e.getMessage());
        }
    }
}

✅ 第二部分:集合框架(Collections Framework)(90分钟)

1. 集合框架概述与JavaScript数组对比

JavaScript (你熟悉的数组和对象)

javascript 复制代码
// JavaScript 数组和对象操作
let fruits = ["apple", "banana", "orange"];
fruits.push("grape");           // 添加元素
fruits.splice(1, 1);           // 删除元素
let hasBanana = fruits.includes("banana"); // 查找元素

let userMap = {
    "user1": "张三",
    "user2": "李四",
    "user3": "王五"
};
userMap["user4"] = "赵六";      // 添加键值对
delete userMap["user2"];        // 删除键值对

// Set数据结构
let uniqueNumbers = new Set([1, 2, 3, 2, 1]);
uniqueNumbers.add(4);
console.log(uniqueNumbers); // Set {1, 2, 3, 4}

Java (今天学习的集合框架)

java 复制代码
import java.util.*;

public class CollectionsDemo {
    
    public static void demonstrateList() {
        System.out.println("=== List 集合演示 ===");
        
        // ArrayList - 动态数组,类似JS Array
        List<String> fruits = new ArrayList<>();
        fruits.add("apple");
        fruits.add("banana");
        fruits.add("orange");
        fruits.add("banana"); // 允许重复元素
        
        System.out.println("初始水果列表: " + fruits);
        
        // 添加元素
        fruits.add(1, "grape"); // 在指定位置插入
        System.out.println("插入葡萄后: " + fruits);
        
        // 删除元素
        fruits.remove("banana");      // 删除第一个匹配的元素
        fruits.remove(2);             // 按索引删除
        System.out.println("删除操作后: " + fruits);
        
        // 查找和检查
        boolean hasApple = fruits.contains("apple");
        int grapeIndex = fruits.indexOf("grape");
        System.out.println("包含苹果: " + hasApple + ", 葡萄索引: " + grapeIndex);
        
        // 遍历方式
        System.out.println("遍历方式1 - 传统for循环:");
        for (int i = 0; i < fruits.size(); i++) {
            System.out.println("  " + i + ": " + fruits.get(i));
        }
        
        System.out.println("遍历方式2 - 增强for循环:");
        for (String fruit : fruits) {
            System.out.println("  " + fruit);
        }
        
        System.out.println("遍历方式3 - Iterator:");
        Iterator<String> iterator = fruits.iterator();
        while (iterator.hasNext()) {
            System.out.println("  " + iterator.next());
        }
        
        // LinkedList vs ArrayList 性能对比示例
        List<Integer> arrayList = new ArrayList<>();
        List<Integer> linkedList = new LinkedList<>();
        
        // ArrayList: 随机访问快,中间插入慢
        // LinkedList: 顺序访问,中间插入快,随机访问慢
    }
    
    public static void demonstrateSet() {
        System.out.println("\n=== Set 集合演示 ===");
        
        // HashSet - 无序,不重复
        Set<String> hashSet = new HashSet<>();
        hashSet.add("Java");
        hashSet.add("Python");
        hashSet.add("JavaScript");
        hashSet.add("Java"); // 重复元素,不会被添加
        
        System.out.println("HashSet (无序): " + hashSet);
        
        // LinkedHashSet - 保持插入顺序,不重复
        Set<String> linkedHashSet = new LinkedHashSet<>();
        linkedHashSet.add("Java");
        linkedHashSet.add("Python");
        linkedHashSet.add("JavaScript");
        linkedHashSet.add("Java");
        
        System.out.println("LinkedHashSet (保持顺序): " + linkedHashSet);
        
        // TreeSet - 自动排序,不重复
        Set<String> treeSet = new TreeSet<>();
        treeSet.add("Java");
        treeSet.add("Python");
        treeSet.add("JavaScript");
        treeSet.add("C++");
        
        System.out.println("TreeSet (自动排序): " + treeSet);
        
        // Set 操作
        Set<Integer> set1 = new HashSet<>(Arrays.asList(1, 2, 3, 4, 5));
        Set<Integer> set2 = new HashSet<>(Arrays.asList(4, 5, 6, 7, 8));
        
        // 交集
        Set<Integer> intersection = new HashSet<>(set1);
        intersection.retainAll(set2);
        System.out.println("交集: " + intersection);
        
        // 并集
        Set<Integer> union = new HashSet<>(set1);
        union.addAll(set2);
        System.out.println("并集: " + union);
        
        // 差集
        Set<Integer> difference = new HashSet<>(set1);
        difference.removeAll(set2);
        System.out.println("差集 (set1 - set2): " + difference);
    }
    
    public static void demonstrateMap() {
        System.out.println("\n=== Map 集合演示 ===");
        
        // HashMap - 键值对,无序
        Map<String, String> userMap = new HashMap<>();
        userMap.put("user1", "张三");
        userMap.put("user2", "李四");
        userMap.put("user3", "王五");
        userMap.put("user1", "张三三"); // 相同key会覆盖value
        
        System.out.println("HashMap: " + userMap);
        
        // Map 基本操作
        String user1Name = userMap.get("user1");
        boolean hasUser2 = userMap.containsKey("user2");
        boolean hasZhangSan = userMap.containsValue("张三");
        
        System.out.println("user1姓名: " + user1Name);
        System.out.println("是否有user2: " + hasUser2);
        System.out.println("是否有张三: " + hasZhangSan);
        
        // 遍历Map的几种方式
        System.out.println("遍历方式1 - keySet:");
        for (String key : userMap.keySet()) {
            System.out.println("  " + key + " -> " + userMap.get(key));
        }
        
        System.out.println("遍历方式2 - entrySet:");
        for (Map.Entry<String, String> entry : userMap.entrySet()) {
            System.out.println("  " + entry.getKey() + " -> " + entry.getValue());
        }
        
        System.out.println("遍历方式3 - values:");
        for (String value : userMap.values()) {
            System.out.println("  " + value);
        }
        
        // LinkedHashMap - 保持插入顺序
        Map<String, Integer> ageMap = new LinkedHashMap<>();
        ageMap.put("张三", 25);
        ageMap.put("李四", 30);
        ageMap.put("王五", 28);
        System.out.println("LinkedHashMap (保持顺序): " + ageMap);
        
        // TreeMap - 按键自动排序
        Map<String, Integer> sortedAgeMap = new TreeMap<>();
        sortedAgeMap.put("张三", 25);
        sortedAgeMap.put("李四", 30);
        sortedAgeMap.put("王五", 28);
        sortedAgeMap.put("赵六", 22);
        System.out.println("TreeMap (按键排序): " + sortedAgeMap);
    }
    
    public static void main(String[] args) {
        demonstrateList();
        demonstrateSet();
        demonstrateMap();
    }
}

2. 集合框架性能对比与选择指南

java 复制代码
import java.util.*;

public class CollectionPerformanceGuide {
    
    public static void performanceComparison() {
        System.out.println("=== 集合性能对比与选择指南 ===");
        
        int dataSize = 100000;
        
        // List性能对比
        System.out.println("\n--- List 性能对比 ---");
        
        // ArrayList: 基于数组实现
        List<Integer> arrayList = new ArrayList<>();
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < dataSize; i++) {
            arrayList.add(i);
        }
        long arrayListAddTime = System.currentTimeMillis() - startTime;
        
        // LinkedList: 基于双向链表实现
        List<Integer> linkedList = new LinkedList<>();
        startTime = System.currentTimeMillis();
        for (int i = 0; i < dataSize; i++) {
            linkedList.add(i);
        }
        long linkedListAddTime = System.currentTimeMillis() - startTime;
        
        System.out.println("ArrayList 添加" + dataSize + "个元素耗时: " + arrayListAddTime + "ms");
        System.out.println("LinkedList 添加" + dataSize + "个元素耗时: " + linkedListAddTime + "ms");
        
        // 随机访问性能对比
        Random random = new Random();
        int accessCount = 10000;
        
        startTime = System.currentTimeMillis();
        for (int i = 0; i < accessCount; i++) {
            int index = random.nextInt(arrayList.size());
            arrayList.get(index);
        }
        long arrayListAccessTime = System.currentTimeMillis() - startTime;
        
        startTime = System.currentTimeMillis();
        for (int i = 0; i < accessCount; i++) {
            int index = random.nextInt(linkedList.size());
            linkedList.get(index);
        }
        long linkedListAccessTime = System.currentTimeMillis() - startTime;
        
        System.out.println("ArrayList 随机访问" + accessCount + "次耗时: " + arrayListAccessTime + "ms");
        System.out.println("LinkedList 随机访问" + accessCount + "次耗时: " + linkedListAccessTime + "ms");
    }
    
    /**
     * 集合选择指南
     */
    public static void selectionGuide() {
        System.out.println("\n=== 集合选择指南 ===");
        
        System.out.println("List 选择:");
        System.out.println("  ArrayList: 适合频繁随机访问,较少插入删除");
        System.out.println("  LinkedList: 适合频繁插入删除,较少随机访问");
        System.out.println("  Vector: 线程安全的ArrayList,性能较低");
        
        System.out.println("\nSet 选择:");
        System.out.println("  HashSet: 最快的查找性能,无序");
        System.out.println("  LinkedHashSet: 保持插入顺序,查找性能略低于HashSet");
        System.out.println("  TreeSet: 自动排序,查找性能O(log n)");
        
        System.out.println("\nMap 选择:");
        System.out.println("  HashMap: 最快的查找性能,无序");
        System.out.println("  LinkedHashMap: 保持插入顺序,查找性能略低于HashMap");
        System.out.println("  TreeMap: 按键自动排序,查找性能O(log n)");
        System.out.println("  ConcurrentHashMap: 线程安全的HashMap");
    }
    
    public static void main(String[] args) {
        performanceComparison();
        selectionGuide();
    }
}

✅ 第三部分:实战练习 (60分钟)

1. 学生管理系统综合练习

java 复制代码
import java.util.*;

// 学生实体类
class Student {
    private String id;
    private String name;
    private int age;
    private double score;
    
    public Student(String id, String name, int age, double score) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.score = score;
    }
    
    // getter和setter方法
    public String getId() { return id; }
    public void setId(String id) { this.id = id; }
    
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    
    public int getAge() { return age; }
    public void setAge(int age) { this.age = age; }
    
    public double getScore() { return score; }
    public void setScore(double score) { this.score = score; }
    
    @Override
    public String toString() {
        return String.format("Student{id='%s', name='%s', age=%d, score=%.1f}", 
                           id, name, age, score);
    }
    
    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        Student student = (Student) obj;
        return Objects.equals(id, student.id);
    }
    
    @Override
    public int hashCode() {
        return Objects.hash(id);
    }
}

// 自定义业务异常
class StudentManagementException extends Exception {
    public StudentManagementException(String message) {
        super(message);
    }
}

// 学生管理系统
public class StudentManagementSystem {
    private Map<String, Student> students;        // 用于快速查找
    private List<Student> studentList;           // 用于排序和遍历
    private Set<String> usedIds;                 // 用于ID唯一性检查
    
    public StudentManagementSystem() {
        this.students = new HashMap<>();
        this.studentList = new ArrayList<>();
        this.usedIds = new HashSet<>();
    }
    
    // 添加学生
    public void addStudent(Student student) throws StudentManagementException {
        if (student == null) {
            throw new StudentManagementException("学生对象不能为空");
        }
        
        if (usedIds.contains(student.getId())) {
            throw new StudentManagementException("学生ID已存在: " + student.getId());
        }
        
        if (student.getScore() < 0 || student.getScore() > 100) {
            throw new StudentManagementException("成绩必须在0-100之间");
        }
        
        students.put(student.getId(), student);
        studentList.add(student);
        usedIds.add(student.getId());
        
        System.out.println("添加学生成功: " + student);
    }
    
    // 删除学生
    public boolean deleteStudent(String id) throws StudentManagementException {
        if (id == null || id.trim().isEmpty()) {
            throw new StudentManagementException("学生ID不能为空");
        }
        
        Student student = students.remove(id);
        if (student == null) {
            throw new StudentManagementException("未找到ID为 " + id + " 的学生");
        }
        
        studentList.remove(student);
        usedIds.remove(id);
        
        System.out.println("删除学生成功: " + student);
        return true;
    }
    
    // 查找学生
    public Student findStudent(String id) throws StudentManagementException {
        if (id == null || id.trim().isEmpty()) {
            throw new StudentManagementException("学生ID不能为空");
        }
        
        Student student = students.get(id);
        if (student == null) {
            throw new StudentManagementException("未找到ID为 " + id + " 的学生");
        }
        
        return student;
    }
    
    // 更新学生信息
    public void updateStudent(String id, String name, int age, double score) 
            throws StudentManagementException {
        Student student = findStudent(id); // 复用查找方法,会抛出相应异常
        
        if (score < 0 || score > 100) {
            throw new StudentManagementException("成绩必须在0-100之间");
        }
        
        student.setName(name);
        student.setAge(age);
        student.setScore(score);
        
        System.out.println("更新学生信息成功: " + student);
    }
    
    // 按成绩排序
    public List<Student> getStudentsSortedByScore() {
        List<Student> sortedList = new ArrayList<>(studentList);
        sortedList.sort((s1, s2) -> Double.compare(s2.getScore(), s1.getScore())); // 降序
        return sortedList;
    }
    
    // 统计信息
    public void printStatistics() {
        if (studentList.isEmpty()) {
            System.out.println("暂无学生数据");
            return;
        }
        
        double totalScore = 0;
        double maxScore = Double.MIN_VALUE;
        double minScore = Double.MAX_VALUE;
        Student topStudent = null;
        Student bottomStudent = null;
        
        for (Student student : studentList) {
            double score = student.getScore();
            totalScore += score;
            
            if (score > maxScore) {
                maxScore = score;
                topStudent = student;
            }
            
            if (score < minScore) {
                minScore = score;
                bottomStudent = student;
            }
        }
        
        double averageScore = totalScore / studentList.size();
        
        System.out.println("\n=== 学生统计信息 ===");
        System.out.printf("学生总数: %d%n", studentList.size());
        System.out.printf("平均分: %.2f%n", averageScore);
        System.out.printf("最高分: %.1f (%s)%n", maxScore, topStudent.getName());
        System.out.printf("最低分: %.1f (%s)%n", minScore, bottomStudent.getName());
    }
    
    // 显示所有学生
    public void displayAllStudents() {
        if (studentList.isEmpty()) {
            System.out.println("暂无学生数据");
            return;
        }
        
        System.out.println("\n=== 所有学生信息 ===");
        for (Student student : studentList) {
            System.out.println(student);
        }
    }
    
    // 按成绩显示学生排名
    public void displayStudentRanking() {
        List<Student> rankedStudents = getStudentsSortedByScore();
        
        System.out.println("\n=== 学生成绩排名 ===");
        for (int i = 0; i < rankedStudents.size(); i++) {
            Student student = rankedStudents.get(i);
            System.out.printf("第%d名: %s (%.1f分)%n", 
                            i + 1, student.getName(), student.getScore());
        }
    }
    
    // 测试方法
    public static void main(String[] args) {
        StudentManagementSystem sms = new StudentManagementSystem();
        
        try {
            // 添加学生
            sms.addStudent(new Student("S001", "张三", 20, 85.5));
            sms.addStudent(new Student("S002", "李四", 19, 92.0));
            sms.addStudent(new Student("S003", "王五", 21, 78.5));
            sms.addStudent(new Student("S004", "赵六", 20, 88.0));
            sms.addStudent(new Student("S005", "钱七", 22, 95.5));
            
            // 显示所有学生
            sms.displayAllStudents();
            
            // 查找学生
            Student student = sms.findStudent("S003");
            System.out.println("\n查找到学生: " + student);
            
            // 更新学生信息
            sms.updateStudent("S003", "王五", 21, 82.0);
            
            // 显示排名
            sms.displayStudentRanking();
            
            // 显示统计信息
            sms.printStatistics();
            
            // 删除学生
            sms.deleteStudent("S002");
            
            // 再次显示排名
            System.out.println("\n删除学生后的排名:");
            sms.displayStudentRanking();
            
        } catch (StudentManagementException e) {
            System.err.println("操作失败: " + e.getMessage());
        }
        
        // 测试异常情况
        System.out.println("\n=== 异常情况测试 ===");
        try {
            sms.addStudent(new Student("S001", "重复ID", 20, 90)); // ID重复
        } catch (StudentManagementException e) {
            System.err.println("预期异常: " + e.getMessage());
        }
        
        try {
            sms.findStudent("S999"); // 不存在的ID
        } catch (StudentManagementException e) {
            System.err.println("预期异常: " + e.getMessage());
        }
        
        try {
            sms.addStudent(new Student("S999", "无效成绩", 20, 120)); // 无效成绩
        } catch (StudentManagementException e) {
            System.err.println("预期异常: " + e.getMessage());
        }
    }
}

📚 今日学习总结

✅ 掌握的知识点

  1. 异常处理机制:try-catch-finally、异常层次结构、自定义异常
  2. 集合框架核心:List、Set、Map的使用和选择
  3. 性能优化意识:不同集合类型的性能特点
  4. 综合应用能力:异常处理与集合框架的结合使用

🎯 明日预告

第5天将学习:

  • 泛型(Generics)深入理解
  • 文件I/O操作
  • 序列化与反序列化
  • 实战:配置文件读写系统

💡 练习建议

  1. 运行并修改今天的所有示例代码
  2. 尝试自己实现一个图书管理系统
  3. 思考在什么场景下使用哪种集合类型
  4. 练习自定义异常的设计和使用
相关推荐
皮皮林5513 小时前
SpringBoot 加载外部 Jar,实现功能按需扩展!
java·spring boot
rocksun3 小时前
认识Embabel:一个使用Java构建AI Agent的框架
java·人工智能
Java中文社群5 小时前
AI实战:一键生成数字人视频!
java·人工智能·后端
王中阳Go5 小时前
从超市收银到航空调度:贪心算法如何破解生活中的最优决策谜题?
java·后端·算法
shepherd1115 小时前
谈谈TransmittableThreadLocal实现原理和在日志收集记录系统上下文实战应用
java·后端·开源
维基框架5 小时前
Spring Boot 项目整合Spring Security 进行身份验证
java·架构
日月星辰Ace6 小时前
Java JVM 垃圾回收器(四):现代垃圾回收器 之 Shenandoah GC
java·jvm
天天摸鱼的java工程师7 小时前
商品详情页 QPS 达 10 万,如何设计缓存架构降低数据库压力?
java·后端·面试
天天摸鱼的java工程师7 小时前
设计一个分布式 ID 生成器,要求全局唯一、趋势递增、支持每秒 10 万次生成,如何实现?
java·后端·面试
阿杆7 小时前
一个看似普通的定时任务,如何优雅地毁掉整台服务器
java·后端·代码规范