要抓還是要拋?

import java.util.Scanner;
import java.io.FileInputStream;
import java.io.FileNotFoundException;

class FileUtil
{
    public static String readFile(String name)
    {
        StringBuilder builder = new StringBuilder();

        try {
            Scanner scanner = new Scanner(new FileInputStream(name));

            while (scanner.hasNext()) {
                builder.append(scanner.nextLine());
                builder.append('\n');
            }
        } catch (FileNotFoundException ex) {
            ex.printStackTrace();
        }

        return builder.toString();
    }

    public static void main(String args[])
    {
    }

}

由於建構FileInputStream時,API設計者聲明方法實作中會拋出FileNotFoundException,根據目前你學到的例外處理語法,於是你捕捉FileNotFoundException並在主控臺中顯示錯誤訊息。

主控臺?等一下!老闆有說這個程式庫會用在文字模式中嗎?如果這個程式庫是用在Web網站上,發生錯誤時顯示在主控臺上,Web使用者怎麼會看得到?你開發的是程式庫,例外發生時如何處理,是程式庫使用者才知道,直接在catch中寫死處理例外或輸出錯誤訊息的方式,並不符合需求。

import java.util.Scanner;
import java.io.FileInputStream;
import java.io.FileNotFoundException;

class FileUtil
{
    public static String readFile(String name) throws FileNotFoundException {
        StringBuilder builder = new StringBuilder();
        Scanner scanner = new Scanner(new FileInputStream(name));

        while (scanner.hasNext())
        {
            builder.append(scanner.nextLine());
            builder.append('\n');
        }

        return builder.toString();
    }

    public static void main(String args[])
    {
    }
}

操作物件的過程中如果會拋出受檢例外,但目前環境資訊不足以處理例外,所以無法使用try、catch處理時,可由方法的客戶端依當時呼叫的環境資訊進行處理。為了告訴編譯器這件事實,必須在方法上使用throws宣告此方法會拋出的例外類型或父類型,編譯器才會讓你通過編譯

拋出受檢例外,表示你認為呼叫方法的客戶端有能力且應該處理例外,throws宣告部份,會是API操作介面的一部份,客戶端不用察看原始碼,從API文件上就能直接得知,該方法可能拋出哪些例外

實際上在例外發生時,可使用try、catch處理當時環境可進行的例外處理,當時環境下無法決定如何處理的部份,可以拋出由呼叫方法的客戶端處理。如果想先處理部份事項再拋出,可以如下:

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.Scanner;

public class FileUtil
{
    public static String readFile(String name) throws FileNotFoundException {
        StringBuilder builder = new StringBuilder();

        try
        {
            Scanner scanner = new Scanner(new FileInputStream(name));

            while (scanner.hasNext()) {
                builder.append(scanner.nextLine());
                builder.append('\n');
            }
        } catch (FileNotFoundException ex)
        {
            ex.printStackTrace();
            throw ex;
        }

        return builder.toString();
    }

    public static void main(String args[])
    {
    }
}

範例在catch區塊進行完部份錯誤處理之後,可以使用throw(注意不是throws)將例外再拋出,實際上,你可以在任何流程中拋出例外,不一定要在catch區塊中,在流程中拋出例外,就直接跳離原有的流程,可以拋出受檢或非受檢例外

記得!

如果拋出的是受檢例外,表示你認為客戶端有能力且應處理例外,此時必須在方法上使用throws宣告

如果拋出的例外是非受檢例外,表示你認為客戶端呼叫方法的時機出錯了,拋出例外是要求客戶端修正這個臭蟲再來呼叫方法,此時也就不用使用throws宣告

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.Scanner;
import java.io.EOFException;

public class FileUtil
{
    public static void doSome(String arg) throws FileNotFoundException, EOFException {
        try
        {
            if ("one".equals(arg)) {
                throw new FileNotFoundException();
            } else {
                throw new EOFException();
            }
        } catch (FileNotFoundException ex)
        {
            ex.printStackTrace();
            throw ex;
        } catch (EOFException ex)
        {
            ex.printStackTrace();
            throw ex;
        }
    }

    public static void main(String args[])
    {
    }
}

如果使用繼承時,父類別某個方法宣告throws某些例外,子類別重新定義該方法時可以:

  • 不宣告throws任何例外
  • 可throws父類別該方法中宣告的某些例外
  • 可throws父類別該方法中宣告例外之子類別

但是不可以:

  • throws父類別方法中未宣告的其它例外
  • throws父類別方法中宣告例外之父類別