在上一教程中,我们了解了异常。异常是程序执行期间发生的意外事件。
捕捉和处理异常
在Java中,我们使用异常处理程序组件try
, catch
和finally
块来处理异常。
为了捕获和处理异常,我们在可能会生成异常的代码周围放置try...catch...finally
块。 finally
块是可选的。
try...catch...finally
的语法try...catch...finally
是:
try {
// code
} catch (ExceptionType e) {
// catch block
} finally {
// finally block
}
Java try … catch块
可能产生异常的代码放在try
块中。
每个try
块应紧随catch
或finally
块。发生异常时,它会被紧随其后的catch
块catch
。
catch
块不能单独使用,并且必须始终在try
块之前。
示例1:try … catch块
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
在这个例子中
- 我们在try块中将数字除以0。这将产生
ArithmeticException
。 - 发生异常时,程序将跳过
try
块中的其余代码。 - 在这里,我们创建了一个catch块来处理
ArithmeticException
。因此,执行catch
块内的语句。
如果try
块中的所有语句均未生成异常,则跳过catch
块。
多个捕获块
对于每个try
块,可以有零个或多个catch
块。
每个catch
块的参数类型指示可以处理的异常类型。多个catch
块使我们能够以不同方式处理每个异常。
示例2:多个捕获块
class ListOfNumbers {
public int[] arrayOfNumbers = new int[10];
public void writeList() {
try {
arrayOfNumbers[10] = 11;
} catch (NumberFormatException e1) {
System.out.println("NumberFormatException => " + e1.getMessage());
} catch (IndexOutOfBoundsException e2) {
System.out.println("IndexOutOfBoundsException => " + e2.getMessage());
}
}
}
class Main {
public static void main(String[] args) {
ListOfNumbers list = new ListOfNumbers();
list.writeList();
}
}
输出
IndexOutOfBoundsException => Index 10 out of bounds for length 10
在此示例中,我们声明了一个大小为10的整数arrayOfNumbers数组。
我们知道数组索引总是从0开始。因此,当我们尝试为索引10分配一个值时,就会发生IndexOutOfBoundsException
,因为arrayOfNumbers的数组范围是0到9。
当try
块中发生异常时,
- 异常被引发到第一个
catch
块。第一个catch
块不处理IndexOutOfBoundsException
,因此将其传递到下一个catch
块。 - 上面示例中的第二个
catch
块是适当的异常处理程序,因为它处理IndexOutOfBoundsException
。因此,它被执行。
Java终于阻止了
对于每个try
块,只能有一个finally
块。
finally
块是可选的。但是,如果已定义,它将始终执行(即使不会发生异常)。
如果发生异常,则在try...catch
块之后执行该异常。如果没有异常发生,则在try
块之后执行。
finally
块的基本语法为:
try {
//code
} catch (ExceptionType1 e1) {
// catch block
} catch (ExceptionType1 e2) {
// catch block
} finally {
// finally block always executes
}
示例3:最终阻止
class Main {
public static void main(String[] args) {
try {
int divideByZero = 5 / 0;
} catch (ArithmeticException e) {
System.out.println("ArithmeticException => " + e.getMessage());
} finally {
System.out.println("Finally block is always executed");
}
}
}
输出
ArithmeticException => / by zero
Finally block is always executed
在此示例中,我们将数字除以0。这引发了一个catch
块捕获的ArithmeticException
。 finally
块始终执行。
进行finally
封锁被认为是一种好习惯。这是因为它包含重要的清除代码,例如:
-
return
,continue
或break
语句可能会意外跳过的代码 - 关闭文件或连接
我们已经提到,最后总是执行,通常就是这种情况。但是,在某些情况下, finally
块不执行:
- 使用
System.exit()
方法 -
finally
块中发生异常 - 线程的死亡
示例4:尝试,捕获并最终阻止
让我们举一个例子,我们尝试使用FileWriter
创建一个新文件,并使用PrintWriter
向其中写入数据。
import java.io.*;
class ListOfNumbers {
private int[] list = new int[10];
public ListOfNumbers() {
// storing integer values in the list array
for (int i = 0; i < 10; i++) {
list[i] = i;
}
}
}
public void writeList() {
PrintWriter out = null;
try {
System.out.println("Entering try statement");
// creating a new file OutputFile.txt
out = new PrintWriter(new FileWriter("OutputFile.txt"));
// writing values from list array to the new created file
for (int i = 0; i < 10; i++) {
out.println("Value at: " + i + " = " + list[i]);
}
} catch (IndexOutOfBoundsException e1) {
System.out.println("IndexOutOfBoundsException => " + e1.getMessage());
} catch (IOException e2) {
System.out.println("IOException => " + e2.getMessage());
} finally {
// checking if PrintWriter has been opened
if (out != null) {
System.out.println("Closing PrintWriter");
out.close();
} else {
System.out.println("PrintWriter not open");
}
}
}
}
class Main {
public static void main(String[] args) {
ListOfNumbers list = new ListOfNumbers();
list.writeList();
}
}
当您运行此程序时,可能会发生两种可能性:
-
try
块中发生异常 -
try
块正常执行
创建新的FileWriter
可能会发生异常。如果无法创建或写入指定的文件,则抛出IOException
。
当发生异常时,我们将获得以下输出。
Entering try statement
IOException => OutputFile.txt
PrintWriter not open
当没有发生异常并且try
块正常执行时,我们将获得以下输出。
Entering try statement
Closing PrintWriter
创建一个OutputFile.txt
,它将具有以下内容:
Value at: 0 = 0
Value at: 1 = 1
Value at: 2 = 2
Value at: 3 = 3
Value at: 4 = 4
Value at: 5 = 5
Value at: 6 = 6
Value at: 7 = 7
Value at: 8 = 8
Value at: 9 = 9
try … catch的工作…最后详细
让我们尝试在上述示例的帮助下详细了解异常处理的流程。
上图描述了在创建新FileWriter
时发生异常时程序的执行流程。
- 若要转到发生异常的方法,主方法将调用
writeList()
方法,然后再调用FileWriter()
方法来创建新的OutputFile.txt
文件。 - 发生异常时,运行时系统将跳过
try
块中的其余代码。 - 它开始以相反的顺序搜索调用堆栈,以找到合适的异常处理程序。
- 在这里,
FileWriter
没有异常处理程序,因此运行时系统检查调用堆栈中的下一个方法,即writeList
。 -
writeList
方法有两个异常处理程序:一个处理IndexOutOfBoundsException
,另一个处理IOException
。 - 然后,系统按顺序处理这些处理程序。
- 此示例中的第一个处理程序处理
IndexOutOfBoundsException
。这与try
块引发的IOException
不匹配。 - 因此,将检查下一个处理程序,即
IOException
处理程序。这与引发的异常类型匹配,因此catch
块中的代码得以执行。 - 执行异常处理程序后,将执行
finally
块。 - 在这种情况下,由于
FileWriter
发生了异常,因此PrintWriter对象out从未打开过,也不需要关闭。
现在,让我们假设在运行该程序时没有发生异常,并且try
块正常执行。在这种情况下,将创建并写入OutputFile.txt
。
众所周知,无论异常处理如何,都将执行finally
块。由于没有发生异常,因此PrintWriter
是打开的,需要关闭。这是通过finally
块中的out.close()
语句完成的。
捕获多个异常
从Java SE 7和更高版本开始,我们现在可以通过一个catch
块捕获不止一种类型的异常。
这样可以减少代码重复并提高代码的简单性和效率。
catch
块可以处理的每种异常类型都使用竖线|
分隔|
。
其语法为:
try {
// code
} catch (ExceptionType1 | Exceptiontype2 ex) {
// catch block
}
要了解更多信息,请访问Java捕获多个异常。
try-with-resources语句
try-with-resources语句是一种try语句,具有一个或多个资源声明。
其语法为:
try (resource declaration) {
// use of the resource
} catch (ExceptionType e1) {
// catch block
}
资源是在程序结束时要关闭的对象。必须在try语句中声明和初始化它。
让我们举个例子。
try (PrintWriter out = new PrintWriter(new FileWriter(“OutputFile.txt")) {
// use of the resource
}
try-with-resources语句也称为自动资源管理 。该语句在语句末尾自动关闭所有资源。
要了解更多信息,请访问Java try-with-resources语句。