异常处理:程序维护的关键

1. 异常基础:什么是异常及其重要性

异常(Exception)是Java中表示错误或异常情况的对象,它会中断程序的正常执行流程。异常机制使程序能优雅处理错误,避免崩溃,并提升代码可读性和可维护性。

关键概念:

  • 常见异常场景(来自文档):

    • 算术错误: 如除零操作(ArithmeticException)。
    • 输入问题: 如无效用户输入(InputMismatchException)。
    • 访问问题: 如无效数组索引(ArrayIndexOutOfBoundsException)或空引用访问(NullPointerException)。
  • 为什么使用异常处理:

    • 防止系统崩溃:错误被捕获后,程序可继续运行(如显示错误消息而非终止)。
    • 隔离错误管理:业务逻辑与错误处理代码分离,提升可读性。
    • 确保连续性:程序在错误后能恢复执行(如重新提示用户输入)。
    • 提升代码质量:通过结构化处理,使代码更健壮和可维护。

场景示例:数组访问问题
当访问无效数组索引时,Java抛出ArrayIndexOutOfBoundsException。

原始代码:

int[] numbers = {1, 2,  3}; // 数组初始化
int value = numbers[5];     // 索引5超出范围,抛出异常

此错误可通过异常处理修复,例如使用try-catch块捕获异常。

场景示例:用户输入问题
处理用户输入时,无效输入(如非数字值)会导致InputMismatchException。

原始代码:

Scanner scanner = new Scanner(System.in);
System.out.print("Enter a number: ");
int number = scanner.nextInt(); // 输入文本时抛出异常

解决方法:使用try-catch捕获异常,并提示用户重试。


2. 异常处理机制:try-catch-finally和throw

Java通过try-catch-finally块处理异常,throw关键字用于手动抛出异常。核心机制确保错误隔离和资源清理。

2.1 try-catch-finally块

  • 结构:
    • try:包裹可能抛出异常的代码。
    • catch:捕获并处理特定异常。
    • finally:无论是否发生异常,都执行(常用于资源清理)。
  • 执行流程:
    • 无异常时:执行try块后跳过catch,执行finally。
    • 有异常时:立即跳转至匹配的catch块,然后执行finally。
    • finally始终执行,即使try或catch中有return语句。

示例:除法程序(Quotient.java)

原始程序在除零时崩溃:

import java.util.Scanner;
public class Quotient {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        System.out.print("Enter two integers: ");
        int number1 = input.nextInt();
        int number2 = input.nextInt();
        System.out.println(number1 + "/" + number2 + " is " + (number1 / number2)); // 除零时抛出ArithmeticException
    }
}

输出:

  • 输入5 2:输出"5/2 is 2"。
  • 输入3 0:抛出ArithmeticException,程序崩溃。

解决方案1:输入验证(使用if-else)

import java.util.Scanner;
public class QuotientWithValidation {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        System.out.print("Enter two integers: ");
        int number1 = input.nextInt();
        int number2 = input.nextInt();
        if (number2 != 0) {
            System.out.println(number1 + "/" + number2 + " is " + (number1 / number2));
        } else {
            System.out.println("Cannot divide by zero!"); // 处理除零错误
        }
    }
}

优点: 简单直接。
缺点:​​ 错误处理与业务逻辑混杂,不适用于复杂场景。

解决方案2:方法封装(使用System.exit)

import java.util.Scanner;
public class QuotientWithMethod {
    public static int quotient(int number1, int number2) {
        if (number2 == 0) {
            System.out.println("Divisor cannot be zero");
            System.exit(1); // 终止程序
        }
        return number1 / number2;
    }
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        System.out.print("Enter two integers: ");
        int number1 = input.nextInt();
        int number2 = input.nextInt();
        int result = quotient(number1, number2);
        System.out.println(number1 + "/" + number2 + " is " + result);
    }
}

缺点: System.exit()粗暴终止程序,缺乏灵活性。

解决方案3:异常处理(使用try-catch)

import java.util.Scanner;
public class QuotientWithException {
    public static int quotient(int number1, int number2) {
        if (number2 == 0)
            throw new ArithmeticException("Divisor cannot be zero"); // 手动抛出异常
        return number1 / number2;
    }
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        System.out.print("Enter two integers: ");
        int number1 = input.nextInt();
        int number2 = input.nextInt();
        try {
            int result = quotient(number1, number2);
            System.out.println(number1 + "/" + number2 + " is " + result);
        } catch (ArithmeticException ex) {
            System.out.println("Exception: an integer cannot be divided by zero"); // 捕获异常
        }
        System.out.println("Execution continues...");
    }
}

优点:

  • 错误隔离:业务逻辑(除法)与错误处理分离。
  • 程序连续性:异常处理后,继续执行后续代码。
  • 灵活性:可扩展处理多种异常类型。

2.2 throw关键字和异常传播

  • throw: 手动抛出异常对象(如throw new ArithmeticException("message"))。
  • 异常传播: 异常未被捕获时,向上层调用方法传播,直至被处理或导致程序终止。

示例:异常传播

package Exception;
class ExceptionPropagation {
    void m() {
        int data = 50 / 0; // 抛出ArithmeticException
    }
    void n() {
        m(); // 异常传播至n()
    }
    void p() {
        try {
            n(); // 异常传播至p()
        } catch (Exception e) {
            System.out.println("Exception handled: " + e); // 捕获并处理
        }
    }
    public static void main(String[] args) {
        ExceptionPropagation obj = new ExceptionPropagation();
        obj.p();
        System.out.println("Program continues normally...");
    }
}

输出:

Exception handled: java.lang.ArithmeticException: / by zero
Program continues normally...

流程: 异常在m()中抛出,传播至n()和p(),最终被p()的catch块捕获,程序继续执行。

2.3 finally块的作用

示例:Scanner资源清理

import java.util.Scanner;
public class FinallyDemo {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("Please enter a divisor");
        try {
            int divisor = scanner.nextInt();
            int quotient = 10 / divisor;
            System.out.println(quotient);
        } catch (InputMismatchException e) {
            System.err.println("Sorry, your input is invalid.");
        } catch (ArithmeticException e) {
            System.err.println("Sorry, unable to divide by 0");
        } finally {
            scanner.close(); // 无论是否异常,都关闭Scanner
            System.out.println("Scanner closed in finally block.");
        }
    }
}

关键点:

  • finally确保资源(如文件、数据库连接)被清理。
  • 执行场景:
    • 无异常时:执行try后执行finally。
    • 有异常时:执行catch后执行finally。
    • 异常未捕获时:先执行finally,再传播异常。

3. 异常类层次和类型

Java异常基于类层次结构,根类为Throwable。异常分为Error、Exception(checked)和RuntimeException(unchecked)。

异常类层次:

img

详细对比:

  • Checked Exception(必须处理):

    • 编译器强制处理(通过try-catch或throws声明)。
    • 代表可恢复错误,如外部因素(文件不存在、网络中断)。
    • 示例:IOException、SQLException。
  • Unchecked Exception(RuntimeException):

    • 编译器不强制处理,通常由编程错误引起(如空指针、除零)。
    • 示例:NullPointerException、ArithmeticException。
    • 最佳实践:通过代码修复而非捕获(如空值检查)。
  • Error:

    • 系统级严重错误(如内存溢出),JVM抛出,不建议捕获。

4. 自定义异常

自定义异常通过继承Exception类创建,用于特定错误场景(如业务规则违规),提升错误信息的清晰度。

实现步骤:

  1. 创建类继承Exception(或其子类)。
  2. 添加构造器(如带消息的构造器)。
  3. 在代码中通过throw抛出。

示例:基本自定义异常

class MyException extends Exception {
    public MyException(String message) {
        super(message); // 调用父类构造器
    }
}

public class CustomExceptionDemo {
    public static void main(String[] args) {
        try {
            throw new MyException("This is a custom exception!"); // 抛出自定义异常
        } catch (MyException e) {
            System.out.println("Exception caught: " + e.getMessage()); // 捕获并处理
        }
    }
}

输出:

Exception caught: This is a custom exception!

完整示例(带多个构造器):

class CompleteException extends Exception {
    public CompleteException() {
        super(); // 无参构造器
    }
    public CompleteException(String message) {
        super(message); // 带消息构造器
    }
    public CompleteException(Throwable cause) {
        super(cause); // 带原因构造器
    }
    public CompleteException(String message, Throwable cause) {
        super(message, cause); // 带消息和原因构造器
    }
}

// 使用示例
public class CustomExceptionUsage {
    public static void validateAge(int age) throws CompleteException {
        if (age < 18) {
            throw new CompleteException("Age must be at least 18"); // 抛出自定义异常
        }
    }
    public static void main(String[] args) {
        try {
            validateAge(15);
        } catch (CompleteException e) {
            System.out.println("Custom exception handled: " + e.getMessage());
        }
    }
}

适用场景: 当标准异常无法精确描述错误时(如课程注册中的年龄限制)。


6. 最佳实践和总结

关键总结:

  • 异常是对象: 通过类层次管理(Throwable为根)。
  • 结构化处理: 使用try-catch-finally分离错误逻辑,确保资源清理。
  • 传播机制: 异常向上传播,直至被捕获。
  • 类型区分: Checked异常必须处理,unchecked异常建议代码修复。
  • 自定义异常: 提升错误信息的业务相关性。

最佳实践:

  • 库方法异常:检查文档(如Scanner.nextInt可能抛出InputMismatchException)。

    img

  • 资源管理: 始终在finally中关闭资源(如文件、数据库)。

  • 避免滥用: 不要捕获所有异常(如catch(Exception e)),应针对特定类型处理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值