Java 异常处理是使用 Java语言进行软件开发和测试脚本开发时不容忽视的问题之一,是否进行异常处理直接关系到软件的稳定性和健壮性。

Java异常的层次结构

在使用Java异常处理之前我们需要先了解一下Java异常类的层次结构。在Java中,所有的异常类都有一个共同的祖先Throwable(可抛出),Throwable有两个非常重要的子类Exception(异常)、Error(错误)。这两个类各自又包含了各自众多的之类。

Exception

Exception是应用程序中可能的可预测、可恢复的问题,异常一般是在特定的环境下出现。它有一个重要的子类RuntimeException(运行时异常),RuntimeException类及其子类表示JVM常用操作引发的错误。例如,若试图使用空值对象引用、除数为零或数组越界,则分别引发运行时异常(NullPointerException、ArithmeticException)和 ArrayIndexOutOfBoundException。

Error

Error是应用程序中发生了较严重的错误,大多数的错误与代码编写操作的执行无关,而是代码运行时JVM出现的问题,比如,但JVM没有持续操作所需的资源时会抛出OutOfMemoryError。

Java异常处理的分类

Java异常处理可分为三类:可检测异常(checked exception)、非检测异常(unchecked exception)和自定义异常

可检测异常(checked exception)

可检测异常经编译器验证,对于声明抛出异常的任何方法,编译器将强制执行处理或声明规则,例如:sqlExecption 这个异常就是一个检测异常。你连接 JDBC 时,不捕捉这个异常,编译器就通不过,不允许编译。

非检测异常

非检测异常不遵循处理或声明规则。在产生此类异常时,不一定非要采取任何适当操作,编译器不会检查是否已解决了这样一个异常。例如:一个数组为 3 个长度,当你使用下标为3时,就会产生数组下标越界异常。这个异常 JVM 不会进行检测,要靠程序员来判断。有两个主要类定义非检测异常:RuntimeException 和 Error。
Error 子类属于非检测异常,因为无法预知它们的产生时间。若 Java 应用程序内存不足,则随时可能出现 OutOfMemoryError;起因一般不是应用程序的特殊调用,而是 JVM 自身的问题。另外,Error 一般表示应用程序无法解决的严重问题。
RuntimeException 类也属于非检测异常,因为普通 JVM 操作引发的运行时异常随时可能发生,此类异常一般是由特定操作引发。但这些操作在 Java 应用程序中会频繁出现。因此,它们不受编译器检查与处理或声明规则的限制。

自定义异常

自定义异常是为了表示应用程序的一些错误类型,为代码可能发生的一个或多个问题提供新含义。可以显示代码多个位置之间的错误的相似性,也可以区分代码运行时可能出现的相似问题的一个或者多个错误,或给出应用程序中一组错误的特定含义。例如,对队列进行操作时,有可能出现两种情况:空队列时试图删除一个元素;满队列时试图添加一个元素。则需要自定义两个异常来处理这两种情况。

Java异常的处理

在Java中,异常处理的方式有两种:异常处理和声明异常

处理异常:try、catch 和 finally

  • try 块:将一个或者多个语句放入try时,则表示这些语句可能抛出异常。
  • catch 块:当问题出现时,一种选择是定义代码块来处理问题,catch 块的目的便在于此。catch 块是 try 块所产生异常的接收者。基本原理是:一旦生成异常,则 try 块的执行中止,JVM 将查找相应的 JVM。
  • finally 块:还可以定义 finally 块,无论运行 try 块代码的结果如何,该块里面的代码一定运行。在常见的所有环境中,finally 块都将运行。无论 try 块是否运行完,无论是否产生异常,也无论是否在 catch 块中得到处理,finally 块都将执行。
    try-catch-finally 规则:
  • 必须在 try 之后添加 catch 或 finally 块。try 块后可同时接 catch 和 finally 块,但至少有一个块。
  • 必须遵循块顺序:若代码同时使用 catch 和 finally 块,则必须将 catch 块放在 try 块之后。catch 块与相应的异常类的类型相关。
  • 一个 try 块可能有多个 catch 块。若如此,则执行第一个匹配块。
  • 可嵌套 try-catch-finally 结构。
  • 在 try-catch-finally 结构中,可重新抛出异常。
  • 除了下列情况,总将执行 finally 做为结束:JVM 过早终止(调用 System.exit(int));在 finally 块中抛出一个未处理的异常;计算机断电、失火、或遭遇病毒攻击。

    声明异常

    声明异常必须将throws关键字添加至方法签名快的最后位置。
    声明异常的规则:
  • 必须声明方法可抛出的任何可检测异常(checked exception)。
  • 非检测性异常(unchecked exception)不是必须的,可声明,也可不声明。
  • 调用方法必须遵循任何可检测异常的处理和声明规则。若覆盖一个方法,则不能声明与覆盖方法不同的异常。声明的任何异常必须是被覆盖方法所声明异常的同类或子类。

Java 异常处理的应用

说到异常处理的应用,银行取钱这个场景非常适合,在定义银行类时,若取钱数大于余额时需要做异常处理。
定义一个异常类 insufficientFundsException。取钱(withdrawal)方法中可能产生异常,条件是余额小于取额。
处理异常在调用 withdrawal 的时候,因此 withdrawal 方法要声明抛出异常,由上一级方法调用。
异常类:

1
2
3
4
5
6
7
8
9
10
11
12
class InsufficientFundsException extends Exception {
private Bank excepbank; // 银行对象
private double excepAmount; // 要取的钱
InsufficientFundsException(Bank ba, double dAmount) {
excepbank=ba;
excepAmount=dAmount;
}
public String excepMessage() {
String str="The balance is"+excepbank.balance + "\n"+"The withdrawal was"+excepAmount;
return str;
}
}

银行类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Bank {
double balance;// 存款数
Bank (double balance) {
this.balance=balance;
}
public void deposite(double dAmount) {
if(dAmount > 0.0) {
balance += dAmount;
}
}
public void withdrawal(double dAmount) throws InsufficientFundsException{
if (balance < dAmount) {
throw new InsufficientFundsException(this, dAmount);
}
balance = balance - dAmount;
}
public void showBalance(){
System.out.println("The balance is "+(int)balance);
}
}

调用:

1
2
3
4
5
6
7
8
9
10
11
12
public class ExceptionDemo{
public static void main(String args[]) {
try {
Bank ba = new Bank(50);
ba.withdrawal(100);
System.out.println("Withdrawal successful!");
} catch (InsufficientFundsException e) {
System.out.println(e.toString());
System.out.println(e.excepMessage());
}
}
}