以下内容引用自http://wiki.jikexueyuan.com/project/java/exceptions.html:
异常是一个程序执行过程中出现的问题。引起异常的原因包括以下几点:
- 用户输入无效的数据
- 用户打开一个不能被找到的文件
- 网络连接已经丢失或JVM已经耗尽内存
一些异常是由于用户的错误,也有是因为程序员的错误,还有是因为一些物理资源在某些形式上的错误。
在Java中了解异常处理,需要了解异常的三个类别:
- 检测异常(Checked exceptions):一个已检测异常通常是用户错误或是一个程序员不能预见的错误,如果一个文件将要被打开,但系统找不到这个文件,异常就会发生。这些异常在编译时不能被简单忽略。
- 运行时异常(Runtime Exceptions):运行时异常是可以被程序语法避免的。不同于检测异常,运行时异常在编译时可以被忽略。
- 错误(Errors):这并不是异常,但这不是用户或程序员可以控制的。错误经常在代码中被忽略,因为针对一个错误几乎做不了任何事。比如,如果一个堆栈溢出发生时,将会出现一个错误。他们在编译时也会被忽略。
一、异常的层次结构
所有的异常类都是java.lang.Exception类的子类型。异常类都是Throwable类的子类。除了异常类Error类也是由Throwable类产生的的子类。
错误一般不会由Java程序解决。这些条件通常是在Java程序解决不了的错误出现时才会发生。 Errors用来去指示那些运行时环境生成的错误。举例说明:JVM(java虚拟机)是不在内存中的。一般程序不能从错误中恢复。
Exception类含有两个子类:IOException类和RuntimeException类。
二、异常方法
下面是Throwable类的重要方法列表。
方法 | 描述 |
---|---|
public String getMessage() | 返回关于发生异常的细节信息,这些信息在Throwable的构造函数中被初始化 |
public Throwable getCause() | 返回发生异常的原因,由Throwable对象来表示 |
public String toString() | 返回与getMessage()的结果相联系的类的名称 |
public void printStackTrace() | 打印toString()跟踪错误输出流的栈地址的结果。 |
public StackTraceElement [] getStackTrace() | 返回一个数组,其中包含每个元素在栈的地址。元素的索引0代表调用栈的顶部,最后一个元素表示方法调用栈的底部。 |
public Throwable fillInStackTrace() | 用当前栈地址来填充Throwable对象的栈地址,添加到任何先前的栈地址信息。 |
三、捕获异常
一个方法用关键字try和catch来捕获异常。一个try/catch块放置在可能产生异常的代码外。在try/catch块内的代码是被保护的,并且其语法如下:
try { //Protected code }catch(ExceptionName e1) { //Catch block }
关于catch的声明,必须要指明捕获的异常的类型。如果受保护的代码中发生异常,跟在try后面的catch块会被检测。如果异常类型在catch中出现了,那么这个异常会被传递到catch当中,就像实参传递到方法参数当中。
示例:
下面的例子声明了一个含有两个元素的数组。然后代码试图访问数组的第三个元素,这将抛出一个异常。
// File Name : ExcepTest.java import java.io.*; public class ExcepTest{ public static void main(String args[]){ try{ int a[] = new int[2]; System.out.println("Access element three :" + a[3]); }catch(ArrayIndexOutOfBoundsException e){ System.out.println("Exception thrown :" + e); } System.out.println("Out of the block"); } } //这将产生如下的结果 Exception thrown :java.lang.ArrayIndexOutOfBoundsException: 3 Out of the block
四、多个catch块
一个try块可以有多个catch块。多个catch块的语法如下:
try { //Protected code }catch(ExceptionType1 e1) { //Catch block }catch(ExceptionType2 e2) { //Catch block }catch(ExceptionType3 e3) { //Catch block }
前面的语句演示三个catch块,但是可以在一个try后面跟任意数量的catch块。当被保护代码中出现异常时,异常首先会被第一个catch块抓住,如果该异常类型在第一个catch块中列出,便在第一个catch块中被捕获。如果没有列出,便依次向下一个catch块中去匹配,直到找到该异常类型。最后不论是否找到现有的方法都会停止执行,并且抛出异常到上一个方法的栈地址。
示例:
下面是代码段显示如何使用多个try/catch语句。
try { file = new FileInputStream(fileName); x = (byte) file.read(); }catch(IOException i) { i.printStackTrace(); return -1; }catch(FileNotFoundException f) //Not valid! { f.printStackTrace(); return -1; }
五、throws/throw关键词
如果一个方法不能处理检测异常,那么该方法必须用关键字throws来声明。throws关键字在方法名的最后出现。
可以通过关键字throw来抛出一个异常或进行刚捕获到的异常的实例化。
以下方法声明它抛出RemoteException异常:
import java.io.*; public class className { public void deposit(double amount) throws RemoteException { // Method implementation throw new RemoteException(); } //Remainder of class definition }
一个方法可以声明抛出一个以上的异常,这些异常用逗号分隔。例如,下面的方法声明抛出RemoteException和InsufficientFundsException :
import java.io.*; public class className { public void withdraw(double amount) throws RemoteException, InsufficientFundsException { // Method implementation } //Remainder of class definition }
提示:throw用于抛出异常,throws用于告诉调用此方法的方法需要处理这个异常,同样throws可以在调用的方法中不用处理,直接throws给上一层,知道给JVM去处理。
六、关键字finally
finally关键字用于创建跟在try后面的代码块,finally代码块总是会被执行的,不管是怎样的异常发生。
使用finally块允许运行想要执行的任何cleanup-type语句,无论在受保护的代码中发生什么。
finally代码块出现在最后一个catch块之后并且语法如下:
try { //Protected code }catch(ExceptionType1 e1) { //Catch block }catch(ExceptionType2 e2) { //Catch block }catch(ExceptionType3 e3) { //Catch block }finally { //The finally block always executes. }
示例:
public class ExcepTest{ public static void main(String args[]){ int a[] = new int[2]; try{ System.out.println("Access element three :" + a[3]); }catch(ArrayIndexOutOfBoundsException e){ System.out.println("Exception thrown :" + e); } finally{ a[0] = 6; System.out.println("First element value: " +a[0]); System.out.println("The finally statement is executed"); } } } //这将产生如下结果: Exception thrown :java.lang.ArrayIndexOutOfBoundsException: 3 First element value: 6 The finally statement is executed
以下几点需要注意:
- catch在没有try关键字的情况下是不能够出现的。
- try/catch语句块中并不是强制出现finally块。
- try语句块不能独立存在(即没有任何catch和finally块)。
- 在try/catch和finally块之间是不能出现任何代码的。
七、声明自己的异常
可以在Java中创建自己的异常。编写自己的异常类,要记住以下几点:
- 所有的异常必须Throwable的子类。
- 如果想写一个自动遵守正确处理或声明规则的检测异常,需要继承Exception 类。
- 如果想编写一个运行时异常,需要继承RuntimeException类。
如下可以定义自己的异常类:
class MyException extends Exception{ }
只需要继承Exception类来创建自己的异常类。这些被认为是检测异常。以下InsufficientFundsException类是一个用户定义的异常,同样继承了Exception类,成为检测异常。一个异常类和任何其他类一样,包含字段和方法。
例子:
// File Name InsufficientFundsException.java import java.io.*; public class InsufficientFundsException extends Exception { private double amount; public InsufficientFundsException(double amount) { this.amount = amount; } public double getAmount() { return amount; } }
为了演示使用用户定义的异常,下面的CheckingAccount类包含一个withdraw()方法抛出了一个InsufficientFundsException异常。
// File Name CheckingAccount.java import java.io.*; public class CheckingAccount { private double balance; private int number; public CheckingAccount(int number) { this.number = number; } public void deposit(double amount) { balance += amount; } public void withdraw(double amount) throws InsufficientFundsException { if(amount <= balance) { balance -= amount; } else { double needs = amount - balance; throw new InsufficientFundsException(needs); } } public double getBalance() { return balance; } public int getNumber() { return number; } }
下面的BankDemo程序演示了调用CheckingAccount的deposit()和withdraw()方法。
// File Name BankDemo.java public class BankDemo { public static void main(String [] args) { CheckingAccount c = new CheckingAccount(101); System.out.println("Depositing $500..."); c.deposit(500.00); try { System.out.println(" Withdrawing $100..."); c.withdraw(100.00); System.out.println(" Withdrawing $600..."); c.withdraw(600.00); }catch(InsufficientFundsException e) { System.out.println("Sorry, but you are short $" + e.getAmount()); e.printStackTrace(); } } }
编译上述三个文件并运行 BankDemo,将产生以下结果:
Depositing $500... Withdrawing $100... Withdrawing $600... Sorry, but you are short $200.0 InsufficientFundsException at CheckingAccount.withdraw(CheckingAccount.java:25) at BankDemo.main(BankDemo.java:13)
八、常见的异常
在Java中,可以定义两种异常和错误
- JVM异常:这些异常/错误是由JVM在逻辑层上或专门抛出的。例如:NullPointerException,ArrayIndexOutOfBoundsException,ClassCastException。
- 程序性异常:这些异常是由应用程序或是编写API的程序员明确抛出的,例如:IllegalArgumentException,IllegalStateException。
测试工程:https://github.com/easonjim/5_java_example/tree/master/javabasicstest/test15