Java 异常操作

捕获异常

使用 try 和 catch 关键字可以捕获异常。try/catch 代码块放在异常可能发生的地方。try/catch 代码块中的代码称为保护代码,使用 try/catch 的语法如下:

try
{
   // 程序代码
}catch(ExceptionName e1)
{
   //Catch 块
}Copy to clipboardErrorCopied

Catch 语句包含要捕获异常类型的声明。当保护代码块中发生一个异常时,try 后面的 catch 块就会被检查。如果发生的异常包含在 catch 块中,异常会被传递到该 catch 块,这和传递一个参数到方法是一样。

譬如下面的例子中声明有两个元素的一个数组,当代码试图访问数组的第三个元素的时候就会抛出一个异常

// 文件名 : 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
**/Copy to clipboardErrorCopied

对于异常的捕获不应该觉得方便而将几个异常合成一个 Exception 进行捕获,比如有 IO 的异常跟 SQL 的异常,这样完全不同的两个异常应该分开处理!而且在 catch 里处理异常的时候不要简单的 e.printStackTrace(),而是应该进行详细的处理。比如进行 console 打印详情或者进行日志记录。

多重捕获块

一个 try 代码块后面跟随多个 catch 代码块的情况就叫多重捕获。多重捕获块的语法如下所示:

try{
   // 程序代码
}catch(异常类型1 异常的变量名1){
  // 程序代码
}catch(异常类型2 异常的变量名2){
  // 程序代码
}catch(异常类型2 异常的变量名2){
  // 程序代码
}Copy to clipboardErrorCopied

上面的代码段包含了 3 个 catch 块。可以在 try 语句后面添加任意数量的 catch 块。如果保护代码中发生异常,异常被抛给第一个 catch 块,如果抛出异常的数据类型与 ExceptionType1 匹配,它在这里就会被捕获。如果不匹配,它会被传递给第二个 catch 块,如此,直到异常被捕获或者通过所有的 catch 块。

try {
    file = new FileInputStream(fileName);
    x = (byte) file.read();
} catch(FileNotFoundException f) { // Not valid!
    f.printStackTrace();
    return -1;
} catch(IOException i) {
    i.printStackTrace();
    return -1;
}Copy to clipboardErrorCopied

异常抛出

如果一个方法没有捕获到一个检查性异常,那么该方法必须使用 throws 关键字来声明。throws 关键字放在方法签名的尾部。也可以使用 throw 关键字抛出一个异常,无论它是新实例化的还是刚捕获到的。

public void test() throws Exception {
    throw new Exception();
}Copy to clipboardErrorCopied

从上面这一段代码可以明显的看出两者的区别。throws 表示一个方法声明可能抛出一个异常,throw 表示此处抛出一个已定义的异常(可以是自定义需继承 Exception,也可以是 Java 自己给出的异常类)。下面方法的声明抛出一个 RemoteException 异常:

import java.io.*;
public class className
{
  public void deposit(double amount) throws RemoteException
  {
    // Method implementation
    throw new RemoteException();
  }
  //Remainder of class definition
}Copy to clipboardErrorCopied

一个方法可以声明抛出多个异常,多个异常之间用逗号隔开。例如,下面的方法声明抛出 RemoteException 和 InsufficientFundsException:

import java.io.*;
public class className
{
   public void withdraw(double amount) throws RemoteException,
                              InsufficientFundsException
   {
       // Method implementation
   }
   //Remainder of class definition
}Copy to clipboardErrorCopied

finally 关键字

finally 关键字用来创建在 try 代码块后面执行的代码块。无论是否发生异常,finally 代码块中的代码总会被执行。在 finally 代码块中,可以运行清理类型等收尾善后性质的语句。finally 代码块出现在 catch 代码块最后,语法如下:

try{
  // 程序代码
}catch(异常类型1 异常的变量名1){
  // 程序代码
}catch(异常类型2 异常的变量名2){
  // 程序代码
}finally{
  // 程序代码
}Copy to clipboardErrorCopied

其示例如下:

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
**/Copy to clipboardErrorCopied

注意,catch 不能独立于 try 存在、在 try/catch 后面添加 finally 块并非强制性要求的、try 代码后不能既没 catch 块也没 finally 块、 try, catch, finally 块之间不能添加任何代码。

finally 与 return

若有 finally 则在 catch 处理后执行 finally 里面的代码。然而存在这样两个问题:

try{
    //待捕获代码
}catch(Exception e){
    System.out.println("catch is begin");
    return 1;
}finally{
     System.out.println("finally is begin");
}Copy to clipboardErrorCopied

在 catch 里面有一个 return,那么 finally 会不会被执行呢?答案是肯定的,上面代码的执行结果为:

catch is begin
finally is beginCopy to clipboardErrorCopied

也就是说会先执行 catch 里面的代码后执行 finally 里面的代码最后才 return1;而如下的代码:

try{
   //待捕获代码
}catch(Exception e){
    System.out.println("catch is begin");
    return 1;
}finally{
     System.out.println("finally is begin");
     return 2 ;
}Copy to clipboardErrorCopied

返回的是 return 2;原因很明显,就是执行了 finally 后已经 return 了,所以 catch 里面的 return 不会被执行到。也就是说 finally 永远都会在 catch 的 return 前被执行。