Java学习十五—异常处理

149 阅读7分钟

一、关于异常

1.1 简介

在Java中,异常(Exception)是指程序执行过程中可能遇到的意外或错误情况。

Java异常处理是Java语言中一个重要的机制,用于处理程序运行时发生的异常情况。

image​​

1.2 分类

Java异常分为两大类:

  1. 编译时异常(Checked Exceptions) :这些异常需要在编写代码时显式地处理或声明抛出。编译时异常通常是可恢复的,比如IOException​、SQLException​等。
  2. 运行时异常(Runtime Exceptions) :这些异常是不可查的,也就是说,它们不需要在代码中显式地进行捕获或声明抛出。运行时异常通常是编程错误导致的,比如NullPointerException​、ArrayIndexOutOfBoundsException​等。

1.3 体系结构

Java异常体系结构是Java语言中处理错误和异常情况的核心机制。Java异常体系基于类层次结构,所有异常类都是Throwable​类的子类。下面是Java异常体系结构的详细介绍:

1. Throwable类

Throwable​ 是所有错误或异常的超类。它定义了所有异常和错误共有的属性和方法,例如错误消息、异常链等。Throwable​类有两个主要的子类:

  • Error​:表示编译时和系统错误(如OutOfMemoryError​、StackOverflowError​等),通常是不可恢复的。
  • Exception​:表示程序运行时可以捕获并处理的异常。

2. Error类

Error​ 类用于指示合理的应用程序不应该尝试捕获的严重问题。大多数这种类型的错误都是与JVM(Java虚拟机)相关的问题,如资源耗尽错误(VirtualMachineError​)或系统行为错误(AssertionError​)。

3. Exception类

Exception​ 类是程序能够处理的异常情况的超类。它进一步分为两大类:

  • 编译时异常(Checked Exceptions) :必须在方法调用时被捕获或声明抛出。这些异常通常是可预见的,比如IOException​、SQLException​等。
  • 运行时异常(Runtime Exceptions) :不需要被捕获或声明抛出,通常是编程错误导致的,如NullPointerException​、IllegalArgumentException​等。​​

img

二、异常处理机制

2.1 关键字

Java的异常处理机制主要包括以下几个关键字:

  • try​:用于定义一个代码块,这个代码块中可能抛出异常。

  • catch​:用于捕获try​块中抛出的异常,并进行处理。

  • finally​:无论是否发生异常,finally​块中的代码都会执行,常用于资源清理工作。

  • throw​:用于在代码中手动抛出一个异常。

  • throws​:用于声明方法可能抛出的异常。

2.2 try catch

在Java中,try-catch​语句是一种异常处理机制,用于捕获和处理可能在程序执行过程中发生的异常。这种机制允许开发者在代码中指定一个或多个代码块,这些代码块在执行时如果抛出了异常,可以被立即捕获并进行相应的处理。

组成部分

  1. try块:包含可能会抛出异常的代码。如果try​块中的代码执行过程中抛出了异常,那么这个异常会被后续的catch​块捕获。
  2. catch块:用于捕获并处理特定的异常类型。可以有多个catch​块,每个catch​块处理不同类型的异常。catch​块的顺序很重要,因为Java会从上到下检查异常类型,一旦找到匹配的异常类型,就会执行相应的catch​块。
  3. finally块:无论是否发生异常,finally​块中的代码都会被执行。这通常用于执行清理操作,比如关闭文件流或释放资源,即使在try​块中发生了异常也是如此。

示例

public class Example {
    public static void main(String[] args) {
        try {
            int[] numbers = {1, 2, 3};
            System.out.println(numbers[4]); // 这将抛出ArrayIndexOutOfBoundsException
        } catch (ArrayIndexOutOfBoundsException e) {
            System.err.println("数组越界异常:" + e.getMessage());
        } finally {
            System.out.println("无论是否发生异常,这段代码都会执行。");
        }
    }
}

2.3 throw

在 Java 中,throw​ 关键字用于显式地抛出异常。通过 throw​ 可以在代码中指定某个特定的异常被抛出,让调用者或者上层调用处理这个异常。

示例

public class ThrowExample {
  
    // 一个方法,当输入的参数小于0时抛出异常
    public void checkValue(int value) throws IllegalArgumentException {
        if (value < 0) {
            // 抛出IllegalArgumentException异常
            throw new IllegalArgumentException("Value cannot be negative.");
        }
        System.out.println("Value is valid: " + value);
    }
​
    public static void main(String[] args) {
        ThrowExample example = new ThrowExample();
        try {
            // 尝试使用一个负数调用checkValue方法
            example.checkValue(-10);
        } catch (IllegalArgumentException e) {
            // 捕获并处理抛出的IllegalArgumentException
            System.err.println("Caught an exception: " + e.getMessage());
        }
    }
}

在这个例子中,checkValue​方法接受一个整数参数。如果这个参数小于0,方法就会使用throw​关键字抛出一个IllegalArgumentException​异常。异常的构造函数中包含了一个错误消息,这个信息在异常被捕获时非常有用。

main​方法中,调用了checkValue​方法,并传递了一个负数。由于这个调用可能会抛出异常,我们使用try-catch​块来捕获和处理这个异常。在catch​块中,我们捕获了IllegalArgumentException​并打印了异常的消息。

2.4 throws

在Java中,throws​关键字用于在方法签名中声明该方法可能会抛出的异常。使用throws​可以让方法的调用者知道在执行该方法时可能会遇到哪些异常情况,并要求调用者对这些异常进行适当的处理。

  • 声明异常:使用throws​关键字在方法签名中声明方法可能会抛出的异常类型。
  • 责任转移:通过声明异常,方法将异常处理的责任转移给调用者,调用者需要捕获或进一步声明抛出这些异常。
  • 编译时检查:声明的异常类型必须是Exception​或其子类的实例,编译器会检查这些异常是否被适当地处理。

示例

public class ThrowsExample {
​
    // 声明一个可能会抛出IOException的方法
    public void readFile(String fileName) throws IOException {
        try {
            // 假设这里有读取文件的代码
            // 如果文件不存在或发生其他I/O错误,将抛出IOException
            throw new IOException("模拟文件读取异常");
        } catch (IOException e) {
            // 可以在这里处理异常,或者不处理,让异常被抛出
            throw e; // 重新抛出异常,让调用者处理
        }
    }
​
    public static void main(String[] args) {
        ThrowsExample example = new ThrowsExample();
        try {
            // 调用可能会抛出IOException的方法
            example.readFile("nonexistentfile.txt");
        } catch (IOException e) {
            // 捕获并处理IOException
            System.err.println("发生I/O异常:" + e.getMessage());
        }
    }
}

在这个示例中,readFile​方法声明它可能会抛出IOException​。这意味着调用readFile​的代码必须处理这个异常,要么通过try-catch​块捕获它,要么在自己的方法签名中使用throws​关键字进一步声明抛出这个异常。

三、自定义异常

3.1 简介

在Java中,自定义异常是指开发者根据特定需求创建的异常类。自定义异常通常用于表示特定的错误情况,这些情况可能不是由Java标准异常类覆盖的。自定义异常可以提供更清晰的错误信息,并且可以与应用程序的其他部分更紧密地集成。

自定义异常通常继承自以下两个类之一:

  • Exception​:如果你的自定义异常是可检查的(checked),即需要调用者显式处理的异常,它应该继承自Exception​类。
  • RuntimeException​:如果你的自定义异常是不可检查的(unchecked),即不需要调用者显式处理的异常,它应该继承自RuntimeException​类。

自定义异常类可以包含额外的方法和属性,以提供更多关于异常情况的信息。

3.2 示例

// 自定义异常类,继承自Exception
public class MyCustomException extends Exception {
    // 构造函数,接收错误消息
    public MyCustomException(String message) {
        super(message);
    }
​
    // 可以添加更多的构造函数,例如接收Throwable类型的cause
    public MyCustomException(String message, Throwable cause) {
        super(message, cause);
    }
​
    // 自定义方法,提供额外的错误信息
    public void printDetails() {
        System.out.println("Custom Exception Details: " + getMessage());
    }
}
​
// 使用自定义异常的类
public class ExampleUsage {
    public static void main(String[] args) {
        try {
            // 触发自定义异常
            throw new MyCustomException("Something went wrong!");
        } catch (MyCustomException e) {
            System.err.println("Caught an exception: " + e.getMessage());
            // 调用自定义方法
            e.printDetails();
        }
    }
}

在这个示例中,MyCustomException​是一个自定义异常类,它接收一个错误消息作为参数,并提供了一个额外的printDetails​方法来打印更多的错误信息。在ExampleUsage​类中,我们尝试抛出这个自定义异常,并在catch​块中捕获它,然后打印错误信息和详细信息。

自定义异常使得异常处理更加灵活和具体,有助于开发者更好地控制程序的异常流程和错误反馈。