JavaRush /Java 博客 /Random-ZH /Java中的异常(Java Exception)

Java中的异常(Java Exception)

已在 Random-ZH 群组中发布
在日常生活中,有时会出现一些我们没有预料到的情况。例如,您早上起床去上班,寻找手机充电器,但没有。你去卫生间洗脸——水已经停了。我上了车,车却发动不起来。但一个人能够很容易地应对这种不可预见的情况。在本文中,我们将尝试弄清楚 Java 程序如何处理它们。

什么是java异常

在编程世界中,程序执行过程中出现错误和意外情况称为异常。在程序中,由于不正确的用户操作、磁盘上缺少必要的资源或通过网络与服务器的连接丢失,可能会发生异常。程序执行过程中的异常也可能是由于编程错误或 API 使用不当造成的。与我们的世界不同,程序必须清楚地知道在这种情况下该怎么做。Java 为此提供了异常机制。

简单介绍一下关键字try、catch、finally、 throws

Java中的异常处理基于在程序中使用以下关键字:
  • try - 定义可能发生异常的代码块;
  • catch – 定义处理异常的代码块;
  • finally – 定义一个可选的代码块,但如果存在,则无论如何都会执行,无论 try 块的结果如何。
这些关键字用于在程序代码中创建特殊处理结构:try{}catch、try{}catch{}finally、try{}finally{}。
  • throw——用于引发异常;
  • throws——用在方法签名中,警告该方法可能抛出异常。
在 Java 程序中使用关键字的示例:
//method reads a string from the keyboard

public String input() throws MyException {//warn with throws,
// that the method can throw MyException
      BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    String s = null;
// in the try block we enclose the code in which an exception can occur, in this
// if the compiler tells us that the readLine() method of the class
// BufferedReader may throw an I/O exception
    try {
        s = reader.readLine();
// in the catch block we enclose the code for handling the IOException exception
    } catch (IOException e) {
        System.out.println(e.getMessage());
// close the read stream in the finally block
    } finally {
// when closing the stream, an exception is also possible, for example, if it was not opened, so we “wrap” the code in a try block
        try {
            reader.close();
// write exception handling when closing the read stream
        } catch (IOException e) {
            System.out.println(e.getMessage());
        }
    }

    if (s.equals("")) {
// we decided that an empty string could disrupt the work of our program in the future, for example, on the result of this method, we need to call the substring(1,2) method, so we are forced to interrupt the program execution with the generation of our exception type MyException using throw
        throw new MyException("String can not be empty!");
    }
    return s;
}

为什么我们需要异常机制?

让我们看一个现实世界的例子。想象一下,高速公路上有一段路段有一座承载能力有限的应急桥。如果质量超过桥梁承载能力的车辆驶过桥梁,桥梁可能会倒塌,温和地说,驾驶员的情况可能会变得异常。为了防止这种情况发生,道路服务部门提前在道路上安装了警告标志。汽车司机看着警告标志,会将汽车的重量与允许在桥上行驶的重量进行比较。如果超过了,他就会绕道而行。由于道路服务部门的行动,卡车司机首先有机会提前改变路线,其次警告他们主干道上存在危险,最后警告他们不能使用特定条件下的桥梁。
Исключения в Java - 2
能够防止和解决程序中的异常,使其能够继续运行,这是在 Java 中使用异常的原因之一。异常机制还允许您通过验证(检查)传入数据来保护您编写的代码(编程接口)免遭用户误用。现在让我们暂时当一下交通警察。首先,您应该了解驾车者可能遇到麻烦的地方。其次,您需要准备并安装警示标志。最后,您需要提供绕行路线,以防主路线上出现危险。在Java中,异常机制的工作原理与此类似。在程序开发阶段,我们使用try{}块“保护”代码中的危险部分免受异常影响,使用catch{}块提供“备份”路径,并在finally{}块中编写在为任何结果制定计划。当我们无法提供“紧急路线”或者故意把选择权留给用户时,我们至少必须警告他存在危险。为什么?想象一下,当一名司机到达一座紧急桥梁时,如果你没有看到沿途一个警告标志,你就无法开车过去,他会多么愤怒!在编程中,当编写我们的类和方法时,我们总是无法预见其他开发人员在程序中使用它们的上下文,因此我们无法预见解决异常的 100% 正确方法。同时,警告我们代码的用户可能出现异常也是一种很好的做法。Java 的异常机制允许我们通过使用 throws 来做到这一点——本质上是声明我们的方法抛出异常的一般行为,从而让该方法的用户编写代码来处理 Java 中的异常。

“麻烦”警告

当您不打算在方法中处理异常,但想要警告该方法的用户可能出现异常情况时,请使用 throws 关键字。方法签名中的此关键字意味着在某些条件下该方法可能会抛出异常。此警告是方法接口的一部分,并赋予用户自定义异常处理程序的实现的权利。在 throws 之后,我们指示抛出的异常类型。这些通常是Java Exception类的后代。由于Java是面向对象的语言,因此Java中的所有异常都是对象。
Исключения в Java - 3

Java异常层次结构

当程序执行期间发生错误时,JVM 运行时会从 Java 异常层次结构创建所需类型的对象 - 从公共“祖先”(Throwable 类)继承的一组可能的异常。程序中出现的异常情况可以分为两类:
  1. 无法恢复程序进一步正常运行的情况
  2. 恢复是可能的。
第一组包括发生从Error类继承的异常的情况。这些错误是在程序执行期间由于 JVM 故障、内存溢出或系统崩溃而发生的错误。它们通常表明无法使用软件修复的严重问题。Java中的此类异常在编译阶段被归类为未检查异常。该组还包括 RuntimeException - 异常,Exception类的继承者,由 JVM 在程序执行期间生成。它们通常是由编程错误引起的。这些异常在编译时也不受检查,因此无需编写代码来处理它们。第二组包括在编写程序阶段预见到的异常情况,并且必须为此编写处理代码。检查此类异常。Java 开发人员在处理异常时的大部分工作就是处理此类情况。

创建异常

程序执行过程中,异常由JVM抛出或者手动使用throw语句抛出。这会在内存中创建一个异常对象并中断主程序代码的执行,同时 JVM 异常处理程序会尝试找到处理异常的方法。

异常处理

我们在 Java 中提供异常处理的代码块的创建是在程序中使用 try{}catch、try{}catch{}finally、try{}finally{} 构造完成的。
Исключения в Java - 4
当 try 块中引发异常时,将在下面的 catch 块中查找异常处理程序。如果 catch 包含此类异常的处理程序,则控制权将传递给它。如果没有,JVM 在方法调用链中查找该异常类型的处理程序,直到找到合适的 catch。执行 catch 块后,控制权将传递给可选的finally块。如果没有找到合适的 catch 块,JVM 将停止程序执行并显示方法调用堆栈 -堆栈跟踪,其中先前已执行过 finally 块代码(如果存在)。异常处理示例:
public class Print {

     void print(String s) {
        if (s == null) {
            throw new NullPointerException("Exception: s is null!");
        }
        System.out.println("Inside method print: " + s);
    }

    public static void main(String[] args) {
        Print print = new Print();
        List list= Arrays.asList("first step", null, "second step");

        for (String s:list) {
            try {
                print.print(s);
            }
            catch (NullPointerException e) {
                System.out.println(e.getMessage());
                System.out.println("Exception was processed. Program continues");
            }
            finally {
                System.out.println("Inside bloсk finally");
            }
            System.out.println("Go program....");
            System.out.println("-----------------");
        }

    }
    }
主要方法 的结果:
Inside method print: first step
Inside bloсk finally
Go program....
-----------------
Exception: s is null!
Exception was processed. Program continues
Inside bloсk finally
Go program....
-----------------
Inside method print: second step
Inside bloсk finally
Go program....
-----------------
该块finally通常用于关闭在 try 块中打开的流或释放资源。然而,在编写程序时,并不总是能够跟踪所有资源的关闭情况。为了让我们的生活更轻松,Java 开发人员为我们提供了一种结构try-with-resources,可以自动关闭在 try 块中打开的资源。我们的第一个例子可以这样重写try-with-resources
public String input() throws MyException {
    String s = null;
    try(BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))){
        s = reader.readLine();
   } catch (IOException e) {
       System.out.println(e.getMessage());
   }
    if (s.equals("")){
        throw new MyException ("String can not be empty!");
    }
    return s;
}
得益于Java的功能,从版本7开始,我们还可以将不同类型异常的捕获组合在一个块中,使代码更加紧凑和可读。例如:
public String input() {
    String s = null;
    try (BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))) {
        s = reader.readLine();
        if (s.equals("")) {
            throw new MyException("String can not be empty!");
        }
    } catch (IOException | MyException e) {
        System.out.println(e.getMessage());
    }
    return s;
}

结果

Java中异常的使用使我们能够通过使用“备份”路径来提高程序的容错性,通过使用catch块将主代码的逻辑与异常处理代码分开,并且还为我们提供了使用 throws 将异常处理委托给代码用户的机会。
还有什么要读的:

关于 Java 异常的十大问题

评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION