使用 try...catch 处理异常

Java 中 try...catch 语句用于处理异常,防止程序异常终止。在本教程中,我们将通过示例学习 Java 中的 try catch 语句用法。

在本教程中,我们将通过示例学习 Java 中的 try catch 语句用法。

在 Java 中,异常可以分为两种类型:

  • 不受检查的异常: 它们不是在编译时检查,而是在运行时检查。例如: RuntimeException, ArithmeticException , NullPointerException , ArrayIndexOutOfBoundsException , Error 类下的异常等。
  • 受检查的异常: 它们在编译时被检查。例如 IOExceptionInterruptedException 等。

请参阅 Java 异常以了解有关受检查的异常和不受检查的异常的详细信息。

对于受检查的异常,编译期间必须要求使用 try...catch 语句处理异常或者在方法上使用 throws 关键字声明。

try…catch

Java 中 try...catch 语句用于处理异常,防止程序异常终止。

try...catch 语句的语法:

try {
  // code
}
catch(Exception e) {
  // code
}

try 块包含可能产生异常的代码。 catch 块包括 try 块当发生异常时需要执行的代码。

示例:Java try…catch 块

public class Main {
  public static void main(String[] args) {

    try {
      int divideByZero = 5 / 0;
      System.out.println("Rest of code in try block");
    }

    catch (ArithmeticException e) {
      System.out.println("ArithmeticException => " + e.getMessage());
    }
  }
}

输出

ArithmeticException => / by zero

在上面的例子中,注意这一行,

int divideByZero = 5 / 0;

在这里,我们试图将一个数除以 0。在这种情况下,会发生异常。因此,我们将此代码包含在 try 块中。

当程序执行到这行代码时,就会发生 ArithmeticException 异常。异常被 catch 块捕获并执行 catch 块内的代码。

try…finally

我们还可以将 try 块与 finally 块一起使用。

在这种情况下,无论 try 块中是否存在异常, finally 块都会被执行。

示例:Java try…finally 块

public class Main {
  public static void main(String[] args) {
    try {
      int divideByZero = 5 / 0;
    }

    finally {
      System.out.println("Finally block is always executed");
    }
  }
}

输出

Finally block is always executed
Exception in thread "main" java.lang.ArithmeticException: / by zero
	at Main.main(Main.java:6)

在上面的示例中,我们将 try 块与 finally 块一起使用。我们可以看到 try 块内的代码导致了异常。

但是,无论异常如何, finally 块内的代码都会执行。

try…catch…finally

在 Java 中,我们也可以在 try...catch 块之后使用 finally 块。例如,

import java.io.*;

class ListOfNumbers {

  // 整数数组
  private int[] list = {5, 6, 8, 9, 2};

  // 将数组中的元素写入文件
  public void writeList() {
    PrintWriter out = null;

    try {
      System.out.println("Entering try statement");

      // 打开文件: OutputFile.txt
      out = new PrintWriter(new FileWriter("OutputFile.txt"));

      // 向文件 OutputFile.txt 中写入值
      for (int i = 0; i < 7; i++) {
        out.println("Value at: " + i + " = " + list[i]);
      }
    } catch (Exception e) {
      System.out.println("Exception => " + e.getMessage());
    } finally {
      // 检查 PrintWriter 对象是否已经开启
      if (out != null) {
        System.out.println("Closing PrintWriter");
        // 关闭 PrintWriter 对象
        out.close();
      } else {
        System.out.println("PrintWriter not open");
      }
    }
  }
}

public class Main {
  public static void main(String[] args) {
    ListOfNumbers list = new ListOfNumbers();
    list.writeList();
  }
}

输出

Entering try statement
Exception => Index 5 out of bounds for length 5
Closing PrintWriter

在上面的例子中,我们创建了一个名为 list 的数组和一个名为 OutputFile.txt 的文件。在这里,我们试图从数组中读取数据并存储到文件中。

注意下面的代码:

for (int i = 0; i < 7; i++) {
  out.println("Value at: " + i + " = " + list[i]);
}

这里,数组的大小为 5,数组的最后一个元素为 list[4] 。但是,我们试图尝试访问 list[5]list[6] 处的元素 。因此,程序会生成数组越界异常。

由于 finally 块总是被执行,我们在 finally 块中包含了关闭 PrintWriter 代码。

使用 finally 块来包含重要的清理代码(例如关闭文件或连接)是一种很好的做法。

注意:有一些情况下不会执行 finally 块:

  • 使用 System.exit() 方法退出程序
  • finally 块中发生异常
  • 执行线程死掉了

多个 Catch 块

对于每个 try 块,可以有零个或多个 catch 块。多个 catch 块允许我们以不同的方式处理每个异常。

每个 catch 块的参数类型表示它可以处理的异常类型。例如,

class ListOfNumbers {
  public int[] arr = new int[10];

  public void writeList() {

    try {
      arr[10] = 11;
    } catch (NumberFormatException e1) {
      System.out.println("NumberFormatException => " + e1.getMessage());
    } catch (IndexOutOfBoundsException e2) {
      System.out.println("IndexOutOfBoundsException => " + e2.getMessage());
    } catch (Exception e3) {
      System.out.println("Exception => " + e3.getMessage());
    }
  }
}

public class Main {
  public static void main(String[] args) {
    ListOfNumbers list = new ListOfNumbers();
    list.writeList();
  }
}

输出

IndexOutOfBoundsException => Index 10 out of bounds for length 10

在这个例子中,我们已经创建了一个名为 arr 整数数组,数组的大小是 10。由于数组索引从0开始,因此数组的最后一个元素位于 arr[9] 。注意声明,

arr[10] = 11;

在这里,我们试图为索引 10 赋值。因此,发生了 IndexOutOfBoundException 异常。

try 块中发生异常时:

  • 异常被抛出到第一个 catch 块。第一个 catch 块不处理 IndexOutOfBoundsException ,异常被传递到下一个 catch 块。
  • 第二个 catch 块是适当的异常处理程序,因为它处理 IndexOutOfBoundsException。因此,它被执行。

! 注意: 当我们存在多个 catch 块是,越高层的异常类应该靠后,否则会有编译错误:“java: 已捕获到异常错误”

捕获多个异常

从 Java SE 7 及更高版本开始,我们可以用一个 catch 块捕获多种类型的异常。这减少了代码重复并提高了代码的简单性和效率。

catch 块都使用竖线 | 分隔可以处理的异常类型。

它的语法是:

try {
  // code
} catch (ExceptionType1 | Exceptiontype2 ex) {
  // catch block
}

要了解更多信息,请访问 Java 捕获多个异常章节。

try-with-resources 语句

前面提到过,在 finally 块中关闭资源是一种很好的做法,但是经常手工关闭资源也是很繁琐的。而 try-with-resources 语句则解决了这个问题,它能帮助我们自动关闭声明的资源。

它的语法是:

try (resource declaration) {
  // use of the resource
} catch (ExceptionType e1) {
  // catch block
}

resource 是程序结束时要关闭的对象,它必须在 try 语句中声明和初始化。

让我们举个例子:

try (PrintWriter out = new PrintWriter(new FileWriter("OutputFile.txt")) {
  // use of the resource
}

try-with-resources 语句会在语句结束后自动关闭 out 资源。

try-with-resources 语句也被称为自动资源管理。该语句会在语句结束时自动关闭所有资源。

要了解更多信息,请访问 java try-with-resources 语句