Java基础篇--异常处理机制

目录

前言:

异常类层次结构:

Error:

Exception

运行时异常

编译时异常

受检异常与非受检异常

受检异常

非受检异常

总结

Java异常关键字

Throwable类

检查异常:

运行时异常:

异常处理语句:

抛出异常:

自定义异常:


前言:

尽管人人希望自己身体健康,处理的事情都能顺利进行,但在实际生活中总会遇到各种状况,比如感冒发烧,工作时电脑蓝屏、死机等。同样,在程序运行的过程中,也会发生各种非正常状况,例如,程序运行时磁盘空间不足、网络连接中断、被装载的类不存在等。针对这种情况, Java语言引入了异常,以异常类的形式对这些非正常情况进行封装,通过错误处理机制对程序运行时发生的各种问题进行处理。

Java的错误处理机制是通过异常(Exception)来实现的。异常是在程序执行过程中出现的问题或错误的一种表示,它可以帮助我们识别和处理程序中的异常情况,从而保证程序的稳定性和可靠性。

异常类层次结构:

Throwable类是 Java 核心库中所有错误和异常类的根类。它是一个抽象类。Java中的异常被组织成一个类层次结构。Throwable 包含了其线程创建时线程执行堆栈的快照,它提供了 printStackTrace() 等接口用于获取堆栈跟踪数据等信息。

所有的异常都是Throwable类的子类,Throwable又分为两个子类:Error(错误类)和Exception(异常类)。

Error:

Error表示严重的系统级问题,通常是由虚拟机或底层资源引起的。

此类错误一般表示代码运行时 JVM 出现问题。通常有 Virtual MachineError(虚拟机运行错误)、NoClassDefFoundError(类定义错误)等。比如 OutOfMemoryError:内存不足错误;StackOverflowError:栈溢出错误。此类错误发生时,JVM 将终止线程。

这些错误是不受检异常,非代码性错误。因此,当此类错误发生时,应用程序不应该去处理此类错误。按照Java惯例,我们是不应该实现任何新的Error子类的!

Exception

Exception则表示可以被捕获和处理的异常情况,包括运行时异常和编译时异常。

运行时异常

运行时异常是 Java 中的一类异常,包括 RuntimeException 及其子类。它们在程序运行期间可能发生,Java 编译器不会强制要求捕获或声明抛出。常见的运行时异常有:空指针异常(NullPointerException)、数组下标越界异常(ArrayIndexOutOfBoundsExcettion)、类型转换异常(ClassCastException)和算术异常(ArithmeticException)等。运行时异常一般是由程序逻辑错误引起的,在代码中可以选择捕获处理或者通过修改代码来避免产生这些异常。

编译时异常

编译时异常是 Exception 中除了 RuntimeException 及其子类之外的异常。它们需要在代码中进行显式处理,要么通过 throws 声明抛出给上级调用者处理,要么通过 try-catch 语句捕获并进行处理。如果代码中出现了这类异常但没有处理,将无法通过编译。常见的编译时异常有:ClassNotFoundException(没有找到指定的类异常)、IO 异常(IOException)等。

受检异常与非受检异常

Java 的所有异常可以分为受检异常(checked exception)和非受检异常(unchecked exception)。

受检异常

受检异常是在编译时被检查出的异常,编译器要求程序必须显式地处理这些异常,否则会导致编译错误。受检异常通常表示程序可能正常情况下无法处理的错误或特殊情况,需要通过捕获和处理来确保程序的稳定性和可靠性。除了 RuntimeException 及其子类之外的所有 Exception 类型都属于受检异常。(要么使用try-catch捕获,要么使用方法签名中用 throws 关键字抛出,否则编译不通过。)

非受检异常

非受检异常(也称为运行时异常)是不需要显式处理的异常,包括 RuntimeException 和其子类以及 Error (错误)类型。与受检异常不同,非受检异常可以选择捕获和处理,但并不强制要求,即使我们没有try-catch捕获它,也没有使用throws抛出该异常,编译也会正常通过。通常情况下,非受检异常表示程序逻辑错误、资源问题或系统错误等无法恢复或无法处理的异常情况。

总结

总的来说,受检异常用于表示必须处理的已知异常情况,而非受检异常用于表示程序中的错误或意外情况。对于受检异常,我们必须进行捕获和处理;而对于非受检异常,我们可以选择性地处理,但也可以让它们传递给调用者进行处理。

Java异常关键字

  1. try – 用于监听。将要被监听的代码(可能抛出异常的代码)放在try语句块之内,当try语句块内发生异常时,异常就被抛出。
  2. catch – 用于捕获异常。catch用来捕获try语句块中发生的异常。
  3. finally – finally语句块总是会被执行。它主要用于回收在try块里打开的物力资源(如数据库连接、网络连接和磁盘文件)。只有finally块,执行完成之后,才会回来执行try或者catch块中的return或者throw语句,如果finally中使用了return或者throw等终止方法的语句,则就不会跳回执行,直接停止。
  4. throw – 用于抛出异常。
  5. throws – 用在方法签名中,用于声明该方法可能抛出的异常。

Throwable类

在Exception(异常类)类的众多子类中有一个特殊的子类—RuntimeException类,RuntimeException类及其子类用于表示运行时异常。 Exception类的其他子类都用于表示编译时异常。Java提供了大量的异常类,这些类都继承自java.lang.Throwable类。接下来通过一张图展示Throwable类的继承体系。

Throwable类中的常用方法如下表。

方法声明

功能描述

String getMessage()

返回异常的消息字符串 

String toString()

返回异常的简单信息描述

void printStackTrace() 将异常堆栈跟踪输出到标准错误流。
String getLocalizedMessage() 返回异常的本地化描述信息。
void fillInStackTrace() 填充当前异常对象的堆栈跟踪元素。
Throwable getCause() 返回引发当前异常的原因(Throwable 对象)。

除了上述方法之外,Throwable 类还提供其他各种用于获取异常相关信息的方法,如获取异常堆栈跟踪、获取异常类型、判断异常是否被捕获等。

在 Java 异常处理中,可以通过捕获特定的异常类型来处理不同的异常情况,也可以通过多重捕获、异常链等方式对异常进行处理和传递。Throwable 类及其子类提供了一种标准的异常处理机制,帮助开发人员识别和恢复出现的错误或异常情况。

检查异常:

这些异常是在编译时强制检查的异常。它们通常涉及到外部资源的操作,如文件I/O、网络连接等。开发者必须显式地处理或声明抛出这些异常。通过捕获和处理检查异常,可以使代码更加健壮,防止未处理异常导致程序崩溃或不可预测的结果。开发者需要使用 try-catch 块来捕获这些异常,并进行相应的处理。

try…catch具体语法格式如下:

try {
    // 可能引发异常的代码块
} catch (异常类型1 异常变量1) {
    // 处理异常类型1的情况
} catch (异常类型2 异常变量2) {
    // 处理异常类型2的情况
} catch (异常类型3 异常变量3) {
    // 处理异常类型3的情况
}

注意:catch代码块需要一个参数指明它所能够接收的异常类型,这个参数的类型必须是Exception类或其子类。 

运行时异常:

这些异常是不需要显式处理的异常,也不需要在方法签名中声明抛出。它们主要包括空指针引用(NullPointerException)、数组下标越界(ArrayIndexOutOfBoundsException)、类型转换错误(ClassCastException)等。运行时异常通常由程序逻辑错误导致,因此应该通过改进代码逻辑来预防发生这些异常。

下面是一个使用 try-catch 语句模仿数组下标越界的代码示例: 

public class myClass {
    public static void main(String[] args) {
        try {
            int[] numbers = {1, 2, 3};
            System.out.println(numbers[4]); // 数组越界,会引发 ArrayIndexOutOfBoundsException 异常
        } catch (Exception e) {
            System.out.println("捕获到异常:" + e);//输出:捕获到异常:java.lang.ArrayIndexOutOfBoundsException: 4
        }
    }
}

异常处理语句:

Java提供了try-catch-finally的异常处理语句来捕获异常并进行相应的处理。在try块中编写可能会抛出异常的代码,如果发生了对应的异常,就会跳转到与之匹配的catch块中进行处理。catch块可以捕获特定类型的异常,也可以使用多个catch块按顺序处理不同类型的异常。finally块用于定义无论是否发生异常都要执行的代码,例如释放资源、关闭文件等。无论是否有异常发生,finally块中的代码都会执行。这样可以进行必要的清理工作,避免资源泄漏等问题。

注意:finally中的代码块在一种情况下是不会执行的,那就是在try...catch中执行了System.exit(0)语句。System.exit(0)表示退出当前的Java虚拟机,Java虚拟机停止了,任何代码都不能再执行了。

下面是一个使用 try-catch-finally 异常处理语句的代码示例:

public class myClass {
    public static void main(String[] args) {
        try {
            int result = divideNumbers(10, 0); // 调用自定义方法,可能触发 ArithmeticException 异常
            System.out.println("结果:" + result);
        } catch (ArithmeticException e) {
            System.out.println("捕获到异常:" + e);
        } finally {
            System.out.println("无论是否发生异常,finally 块中的代码都会执行");
        }

        System.out.println("try-catch-finally 语句块之外的代码");
    }

    public static int divideNumbers(int a, int b) {
        return a / b; // 若除数为 0,则会触发 ArithmeticException 异常
    }
}

运行结果:

捕获到异常:java.lang.ArithmeticException: / by zero
无论是否发生异常,finally 块中的代码都会执行
try-catch-finally 语句块之外的代码

抛出异常:

在方法中,我们可以使用throw关键字主动抛出异常。通过throw语句,我们可以在任何需要的地方抛出异常,并将其传递给上层调用者来处理。抛出异常的过程中,当前方法的执行将被中断,然后异常被传递给调用堆栈中的上一级调用方法。

throws关键字声明抛出异常的语法格式如下:

修饰符 返回类型 方法名(参数列表) throws 异常类型1, 异常类型2, ... {
    // 方法体
}

其中:

  • 修饰符:表示方法的可见性和其他修饰符(例如 public、private、static 等)。
  • 返回类型:表示方法返回值的类型。
  • 方法名:表示方法的名称。
  • 参数列表:表示方法接收的参数。
  • 异常类型1, 异常类型2, ...:表示方法可能抛出的异常类型。多个异常类型之间使用逗号分隔。

下面是一个使用 throw 关键字的代码示例:

public class myClass {
    public static void main(String[] args) {
        try {
            int result = divideNumbers(10, 0); // 调用自定义方法,可能触发 ArithmeticException 异常
            System.out.println("结果:" + result);
        } catch (ArithmeticException e) {
            System.out.println("捕获到异常:" + e);//输出:捕获到异常:java.lang.ArithmeticException: / by zero
        } finally {
            System.out.println("无论是否发生异常,finally 块中的代码都会执行");//输出:无论是否发生异常,finally 块中的代码都会执行
        }

        System.out.println("try-catch-finally 语句块之外的代码");
    }

    public static int divideNumbers(int a, int b) throws ArithmeticException{
        if (b == 0) {
            throw new ArithmeticException("除数不能为零"); // 抛出 ArithmeticException 异常对象
        } else {
            int result = a / b;
            System.out.println("结果:" + result);
        }
        return a;
    }
}

运行结果:

捕获到异常:java.lang.ArithmeticException: 除数不能为零
无论是否发生异常,finally 块中的代码都会执行
try-catch-finally 语句块之外的代码

自定义异常:

除了Java内置的异常类,开发者还可以根据需要自定义异常类。自定义异常类需要继承Exception或其子类,并根据具体需求添加自己所需的字段和方法。自定义异常可以更好地满足业务需求,提供更准确的异常信息和处理方式。

在实际开发中,如果没有特殊的要求,自定义的异常类只需继承Exception类,在构造方法中使用super()语句调用Exception的构造方法即可。

自定义异常类中使用throw关键字在方法中声明异常的实例对象,格式如下:

throw new Exception异常对象

下面是一个简单的自定义异常类的示例:

// 自定义异常类
class NegativeNumberException extends Exception {
    public NegativeNumberException(String message) {
        super(message);
    }
}

// 示例类
public class myClass {
    // 计算平方根的方法
    public static double calculateSquareRoot(double number) throws NegativeNumberException {
        if (number < 0) {
            throw new NegativeNumberException("输入不能为负数");
        } else {
            // 使用 Math.sqrt() 方法计算平方根
            double squareRoot = Math.sqrt(number);
            return squareRoot;
        }
    }

    // 主方法
    public static void main(String[] args) {
        double input = -5.0;

        try {
            // 调用 calculateSquareRoot 方法
            double result = calculateSquareRoot(input);
            System.out.println("平方根结果:" + result);
        } catch (NegativeNumberException e) {
            System.out.println("捕获到自定义异常:" + e.getMessage());
            // 其他异常处理逻辑
            // ...
        }
    }
}

猜你喜欢

转载自blog.csdn.net/m0_74293254/article/details/132323091
今日推荐