You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

10 KiB

成绩平均分计算项目 - 异常处理学习 (Score Average Calculator - Exception Handling)

项目概述

这是一个Java学习项目,重点强调异常处理的必要性。通过对比两个版本的代码展示:

  • 没有异常处理的代码会如何崩溃
  • 使用异常处理的代码如何优雅应对错误

项目结构

Java源代码文件

ScoreAverage0.java ⚠️

  • 描述:未进行异常处理的版本(用于演示问题)
  • 特点
    • 移除了 throws Exception - 强制必须处理异常
    • 没有 try-catch 块
    • 直接调用会抛异常的方法:
      • FileReader() - 可能抛 FileNotFoundException
      • BufferedReader.readLine() - 可能抛 IOException
      • Integer.parseInt() - 可能抛 NumberFormatException
    • 会导致程序崩溃
  • 适用场景
    • 理解"如果不处理异常会怎样"
    • 强调异常处理的重要性
    • 作为负反例学习

ScoreAverage1.java

  • 描述:改进版本,具备完整的异常处理
  • 特点
    • 使用 try-catch 块捕获三种异常:
      • NumberFormatException:处理数字格式错误
      • FileNotFoundException:处理文件不存在
      • IOException:处理其他IO错误
    • 使用 try-with-resources 自动关闭资源
    • 检查成绩数量,防止零数除
    • 程序不会崩溃
  • 适用场景:生产级代码,具备健壮的错误处理

测试数据文件与异常演示

ScoreAverage0 - 异常演示场景

文件名 包含内容 期望的异常 说明
score_invalid_format.txt 包含 "abc" 等非数字内容 NumberFormatException 当调用 Integer.parseInt("abc")
score.txt 空文件 ArithmeticException 分母为0时,计算平均值异常
(不存在的文件如 missing.txt) 不存在 FileNotFoundException 当文件不存在时

ScoreAverage1 - 异常处理演示

测试场景 使用文件 结果 说明
正常情况 score_valid.txt (85, 92, 78, 88, 95) 计算出平均分 程序正常运行
数字格式错误 score_invalid_format.txt 捕获异常,提示用户 程序不崩溃
空文件 score_empty.txt 提示"No scores to average." 程序不崩溃
文件不存在 (手动改为不存在的名称) 捕获异常,提示用户 程序不崩溃

使用方法 - 异常演示步骤

第一步:观察异常的危害(ScoreAverage0.java)

1. 编译代码

javac ScoreAverage0.java

2. 运行程序并观察崩溃

java ScoreAverage0

预期的错误输出:

Exception in thread "main" java.lang.NumberFormatException: For input string: "abc"
    at java.lang.Integer.parseInt(Integer.java:615)
    at java.lang.Integer.parseInt(Integer.java:676)
    at ScoreAverage0.main(ScoreAverage0.java:14)

问题分析:

  • 程序因为数据格式错误而直接崩溃
  • 没有任何用户友好的错误提示
  • 异常信息对普通用户来说很难理解

第二步:学习异常处理(ScoreAverage1.java)

1. 编译改进版本

javac ScoreAverage1.java

2. 测试场景A - 数字格式错误

java ScoreAverage1

输出(程序不崩溃):

Invalid number format in file: For input string: "abc"
No scores to average.

3. 测试场景B - 正常数据

修改代码中的文件名为 score_valid.txt,然后:

javac ScoreAverage1.java
java ScoreAverage1

输出:

Average score: 88.0
Average score: 88.0

4. 测试场景C - 空文件

修改代码中的文件名为 score_empty.txt,然后运行:

javac ScoreAverage1.java
java ScoreAverage1

输出:

No scores to average.

优势分析:

  • 程序不会崩溃
  • 用户能看到清晰的警告信息
  • 程序能够优雅地处理各种错误情况

核心学习目标

🎯 为什么异常处理很重要?

通过比较两个版本,我们学到:

对比项 ScoreAverage0 ScoreAverage1
程序健壮性 易崩溃 稳定可靠
用户体验 复杂的技术错误 清晰的提示信息
代码质量 不可生产使用 生产级可用
错误来源追踪 困难 清晰明了
程序流程 异常终止 优雅降级

ScoreAverage0 - 问题演示

这个版本展示了:

  • 没有 throws Exception 声明
  • 没有 try-catch 块
  • 直接调用可能抛异常的方法
  • 导致程序崩溃的后果

你将发现:

  1. FileReader("file.txt") - 如果文件不存在会崩溃
  2. Integer.parseInt(line) - 如果数据不是数字会崩溃
  3. br.readLine() - 如果IO错误会崩溃
  4. sum / count - 如果count为0会崩溃

ScoreAverage1 - 解决方案演示

这个版本展示了:

  • try-with-resources 自动管理资源
  • 多个 catch 块处理不同异常
  • 友好的出错提示
  • 程序优雅处理错误

关键技巧:

  1. try-with-resources:自动关闭 BufferedReader

    try (BufferedReader br = new BufferedReader(new FileReader("score.txt"))) {
        // 代码...
    } // 自动调用 br.close()
    
  2. 多 catch 块:按顺序捕获不同异常

    catch (NumberFormatException e) { ... }
    catch (FileNotFoundException e) { ... }
    catch (IOException e) { ... }
    
  3. 防御性编程:检查必要的条件

    if(count > 0) {
        double average = (double) sum / count;
        System.out.println("Average score: " + average);
    } else {
        System.out.println("No scores to average.");
    }
    

深入理解三种异常

1️⃣ NumberFormatException(数字格式异常)

何时发生:Integer.parseInt() 接收到不能解析的字符串时

Integer.parseInt("abc");   // ❌ 异常!
Integer.parseInt("123");   // ✅ 正常
Integer.parseInt("12.5");  // ❌ 异常!浮点数

在本项目中:

  • 文件 score_invalid_format.txt 中包含 "abc"
  • 可以用来触发这个异常

错误消息示例:

java.lang.NumberFormatException: For input string: "abc"

2️⃣ FileNotFoundException(文件不存在异常)

何时发生:FileReader() 无法找到指定的文件时

new FileReader("score.txt");        // ✅ 如果存在
new FileReader("missing.txt");      // ❌ 异常!文件不存在

在本项目中:

  • 如果代码中指定的文件不存在
  • FileNotFoundException 是 IOException 的子类

错误消息示例:

java.io.FileNotFoundException: score.txt (系统找不到指定的文件。)

3️⃣ IOException(输入输出异常)

何时发生:

  • readLine() 读取失败
  • 文件权限问题
  • 磁盘错误
  • 其他 IO 相关问题

在本项目中:

  • 捕获所有其他 IO 异常
  • 也是 FileNotFoundException 的父类

错误消息示例:

java.io.IOException: 磁盘空间不足

⚠️ ArithmeticException(算术异常)

何时发生:

int result = 10 / 0;  // ❌ 异常!除以零

在本项目中:

  • 如果 count = 0,计算 sum / count 会崩溃
  • 这是未经检查的异常(不强制捕获)
  • 解决办法:检查条件后再计算
if (count > 0) {
    double average = (double) sum / count;
} else {
    System.out.println("No scores to average.");
}

运行结果对比

ScoreAverage0 - 不处理异常(会崩溃)

场景1:数据格式错误

Exception in thread "main" java.lang.NumberFormatException: For input string: "abc"
    at java.lang.Integer.parseInt(Integer.java:615)
    at java.lang.Integer.parseInt(Integer.java:676)
    at ScoreAverage0.main(ScoreAverage0.java:14)

程序崩溃了!

场景2:文件不存在

Exception in thread "main" java.io.FileNotFoundException: missing.txt (系统找不到指定的文件。)
    at java.io.FileInputStream.open(FileInputStream.java:195)
    at java.io.FileInputStream.<init>(FileInputStream.java:150)
    at java.io.FileReader.<init>(FileReader.java:72)
    at ScoreAverage0.main(ScoreAverage0.java:6)

程序崩溃了!

ScoreAverage1 - 处理异常(优雅降级)

场景1:数据格式错误 + 正常处理

Invalid number format in file: For input string: "abc"
No scores to average.

程序继续运行,给出清晰提示

场景2:文件不存在

File not found: missing.txt (系统找不到指定的文件。)
No scores to average.

程序继续运行,给出清晰提示

场景3:正常数据

Average score: 88.0
Average score: 88.0

正常计算结果

总结:异常处理的必要性

为什么 ScoreAverage0 需要改进?

问题 说明
程序随时可能崩溃 任何输入错误都会导致 uncaught exception
用户体验差 复杂的堆栈跟踪信息无法理解
难以维护 出错时不知道具体发生了什么
不信任程序 用户无法相信程序的稳定性

为什么 ScoreAverage1 更好?

优势 说明
程序稳定可靠 各种异常都被正确处理
友好的提示 用户能理解发生了什么
易于维护 清晰的日志和错误消息
值得信任 无论输入如何,程序都能应对

编程最佳实践

 好的做法
 捕获可能的异常
 给出有意义的错误提示
 使用 try-with-resources 管理资源
 进行防御性检查如零数检查
 记录异常信息便于调试

 不好的做法
 忽略异常让程序崩溃
 捕获异常但不处理
 忽悠用户隐藏真实错误
 不检查输入的有效性

进阶学习主题

  1. 📚 异常层次结构

    • Throwable → Exception & Error
    • Exception → Checked & Unchecked
    • 了解哪些异常必须捕获
  2. 🔧 try-finally vs try-with-resources

    • 手动资源管理 vs 自动资源管理
    • Java 7+ 推荐使用 try-with-resources
  3. 📝 自定义异常

    • 创建特定的异常类
    • 在适当的位置抛出自定义异常
  4. 💾 日志记录

    • 使用日志框架(如 Log4j)
    • 记录异常堆栈跟踪便于调试
  5. 🔄 异常链

    • 捕获一个异常后抛出另一个异常
    • 保留原始异常的信息