Java 异常讲解
异常
是一种特殊的机制,可以让我们在程序中处理错误。
什么是异常
在编程世界中,程序执行中的错误和不可预见的情况称为 异常
。
在程序中,由于 用户操作无效
、 磁盘空间不足
或 与服务器的网络连接丢失
,可能会发生异常。异常也可能是由 编程错误
或 不正确使用 API
引起的。与现实世界中的人类不同,程序必须确切地知道如何处理这些情况。为此,Java
有一种称为异常处理的机制。
关于关键字
**Java
中的异常处理基于在程序中使用以下关键字:**在 Java
程序中使用关键字的示例:
try
- 定义可能发生异常的代码块;catch
- 定义处理异常的代码块;finally
- 定义一个可选的代码块,如果存在,则执行该代码块,而不管try
块的结果如何。
这些关键字用于在代码中创建特殊构造:try{}catch
try{}catch{}finally
try{}finally{}
throw
- 用于提出异常;throws
- 在方法签名中使用,以警告方法可能引发异常。
// 此方法从键盘读取字符串
public String input() throws MyException { // Use throws to warn
// t该方法可能引发 MyException
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String s = null;
// 我们使用 try 块包装可能会创建异常的代码。在这种情况下,
// 编译器告诉我们
// BufferedReader 类可能引发 I/O 异常
try {
s = reader.readLine();
// 我们使用 catch 块包装处理 IOException 的代码
} catch (IOException e) {
System.out.println(e.getMessage());
// 我们在 finally 块中关闭读取流
} finally {
// 例如,如果流没有打开,那么当我们关闭流时可能会发生异常,因此我们将代码包装在一个 try 块中
try {
reader.close();
// 关闭读取流时处理异常
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
if (s.equals("")) {
// 当存在空字符串时,将阻止我们的程序正常工作。
// 例如,我们使用此方法的结果调用子字符串 (1, 2) 方法。因此,我们必须通过使用 throw 来生成我们自己的 MyException 异常类型来中断程序。
throw new MyException("The string cannot be empty!");
}
return s;
}
为什么我们需要异常?
让我们看一个来自现实世界的例子。想象一下,高速公路的一段有一座重量容量有限的小桥。如果一辆比桥梁极限重的汽车驶过它,它可能会倒塌。委婉地说,司机的情况会变得异常。为了避免这种情况,运输部门在出现问题之前在道路上安装警告标志。看到警告标志,驾驶员将他或她的车辆重量与桥梁的最大重量进行比较。如果车辆太重,驾驶员将采取绕行路线。运输部门首先,使卡车司机能够在必要时改变路线,其次,警告司机主要道路上的危险,第三,警告司机在一定条件下不得使用桥梁。防止和解决程序中异常情况的能力,允许它继续运行,是在 Java
中使用异常的一个原因。
异常机制
还允许您通过验证(检查)任何输入来保护 代码(API)
免受不当使用。现在想象一下,你是运输部门。首先,您需要知道驾驶者可以预期遇到麻烦的地方。其次,您需要创建并安装警告标志。最后,如果主要路线出现问题,您需要提供绕行。
在 Java
中, 异常机制
的工作方式与此类似。在开发过程中,我们使用 try
模块围绕危险的代码段构建 “异常屏障
” ,我们使用 catch
模块提供 “备份路由
” ,并且我们编写代码,无论模块中有什么内容,都应该运行。如果我们不能提供“备份路由”,或者我们想给用户选择的权利,我们至少必须警告他或她危险。
为什么?想象一下,一个司机的愤怒,他没有看到一个警告标志,到达了一座他无法穿过的小桥!
在编程中,在编写类和方法时,我们不能总是预见其他开发人员如何使用它们。因此,我们无法预见 100% 解决特殊情况的正确方法
。也就是说,警告其他人发生特殊情况的可能性是一种很好的形式。而 Java
的 异常机制
允许我们使用 关键字
来做到这一点,其本质上是一个声明,即我们方法的一般行为包括抛出异常。
因此,任何使用该方法的人都知道他或她应该编写代码来处理异常。
try
catch {}
finally{}
throws
警告他人 “麻烦” (throws
Exception
)
如果您不打算在方法中处理异常,但希望警告其他人可能会发生异常,请使用关键字。方法签名中的此关键字意味着,在某些情况下,方法可能会引发异常。
此警告是方法接口的一部分,允许其用户实现自己的异常处理逻辑。抛出后,我们指定抛出的异常类型。
这些通常是 Java
类的后代。由于 Java
是一种面向对象的语言,因此所有例外都是 Java
中的对象。
异常层次结构
当程序运行时发生错误时,JVM
会从 Java
异常层次结构(一组可能源自共同祖先的异常)类中创建一个适当类型的对象。我们可以将异常运行时情况分为两组:Throwable
- 程序无法恢复并继续正常运行的情况。
- 可以恢复的情况。
第一组包括涉及从类中继承的异常的情况。这些是由于 JVM 故障
、内存溢出
或 系统故障
而发生的错误。它们通常表示软件无法修复的严重问题。
在 Java
中,编译器不会检查此类异常的可能性,因此它们被称为 未经检查的 异常。此组还包括 RuntimeExceptions
,它们是从类中降下来的异常,由 JVM
在运行时生成。它们通常是由编程错误引起的。
这些异常也不会在编译时选中(取消选中),因此您无需编写代码来处理它们。
第二组包括编写程序时可以预见的异常情况(因此您应该编写代码来处理它们)。此类异常称为已检查异常。当涉及到异常时,Java
开发人员的大部分工作都是处理这种情况。Error
Exception
创建例外 (throw
)
当程序运行时,异常由 JVM
或使用语句手动生成。发生这种情况时,将在内存中创建异常对象,程序的主流被中断,并且 JVM
的异常处理程序尝试处理异常。
异常处理
在 Java
中,我们创建代码块,在其中我们预测需要使用 , ,
和 构造进行异常处理。当在块中引发异常时,JVM
会在下一个模块中查找适当的异常处理程序。
如果模块具有所需的异常处理程序,则控制权将传递给它。如果不是,则 JVM
会进一步向下查找模块链,直到找到合适的处理程序。执行模块后,控制权将转移到可选模块。如果未找到合适的模块,则 JVM
将停止程序并显示(当前方法调用堆栈),如果存在该块,则在首先执行该模块后显示该模块。
异常处理示例: 以下是该方法的结果:
通常用于关闭任何流并释放在模块中打开/分配的任何资源。但是,在编写程序时,并不总是能够跟踪所有资源的关闭。为了使我们的生活更轻松,Java
的开发人员提供了该结构,该结构会自动关闭块中打开的任何资源。由于 版本7
中引入了 Java
功能,我们还可以将捕获异构异常组合到一个块中,使代码更加紧凑和可读。
例:
try{} catch
try{} catch{} finally
try{} finally{}
public class Print {
void print(String s) {
if (s == null) {
throw new NullPointerException("Exception: s is null!");
}
System.out.println("Inside print method: " + 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 handled. The program will continue");
}
finally {
System.out.println("Inside finally block");
}
System.out.println("The program is running...");
System.out.println("-----------------");
}
}
}
main
Inside print method: first step
Inside finally block
The program is running...
-----------------
Exception: s is null!
Exception handled. The program will continue
Inside finally block
The program is running...
-----------------
Inside print method: second step
Inside finally block
The program is running...
-----------------
finally
try
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 ("The string cannot be empty!");
}
return s;
}
public String input() {
String s = null;
try (BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))) {
s = reader.readLine();
if (s.equals("")) {
throw new MyException("The string cannot be empty!");
}
} catch (IOException | MyException e) {
System.out.println(e.getMessage());
}
return s;
}
底线
通过在 Java
中使用异常,您可以通过创建 “备份路由” 使程序更加健壮,使用 catch
块将主代码与异常处理代码分开,并使用将异常处理的责任转移给使用您的方法的任何人。throws