Skip to content

Diplab/Exceptions

Repository files navigation

CH.12 Error handling with Exceptions

Outline

前言

Error & Exception

程式於執行過程中,發生了預料之外的情況而造成程式無法繼續執行下去,Java將這種錯誤大致上分為兩類

  • error:無法由程式自行進行處理的重大的錯誤如作業系統錯誤、硬碟故障、記憶體故障等等,通常此類情況發生時,你無法做出什麼補救
  • Exception:較輕微可被處理的狀況如欲開啟的檔案不存在,陣列索引為負值等等

Exception-handling的重要

它可以確保系統的可用性與可靠性,提高軟體應付異常狀況的能力,避免系統在意想不到的狀況下發生錯誤,甚至導致服務中斷或資料毀損。 早期的程式語言本身並不支援例外處理,像 C 語言靠的是利用函式回傳值的檢查,如果不檢查很容易讓系統發生錯誤,且容易破壞程式結構。 現代較新的物件導向程式語言,像 Java、C# 或 Visual Basic .NET 則提供了 try...catch...finally陳述式,是一個結構化例外處理(Structured Exception Handling) 機制,不僅可以有效簡化例外處理複雜度,還可維持程式碼可讀性。

程式閱讀困難 且結構被破壞:

public class ReadFile {
	public int Read() {
		int ErrCode = 0;
		開啟檔案;
		if (檔案存在) {
			決定檔案大小;
			if (檔案大小已定) {
				配置記憶體;
				if (記憶體足夠) {
					將檔案讀入記憶體;
					if (讀入成功) {
					開始操作檔案內容; }
					else {
						ErrCode = -1; } }
				else {
					ErrCode = -2; } }
			else {
				ErrCode = -3 }
			關閉檔案;
			if (檔案無法關閉 && 無其它錯誤)
				ErrCode = -4;
			else
				ErrCode = -5; }
		else
			ErrCode = -6;
		return ErrCode;
		}
}

Structured Exception Handling

Public class ReadFile {
		public int Read() {
				try {
					開啟檔案;
					決定檔案大小;
					配置記憶體;
					將檔案讀入記憶體;
					關閉檔案;
				} catch (檔案打不開) {
					return –1;
				} catch (無法決定大小) {
					return –2;
				} catch (記憶體不夠) {
					return –3;
				} catch (檔案讀不進來) {
					return –4;
				} catch (檔案關不掉) {
					return –5
				}
		}
}

Throwable

Java將所有的Exception宣告成物件,且全部都是屬於Throwable物件的子孫 Throwable.jpg

Error

  • 屬於Unchecked Exception
  • 嚴重而無法進行處理的狀況 EX:記憶體故障 OS問題
  • 大多數的應用程式不會嘗試去取得此類資訊也無法處理
  • 由Java virtual machine 進行處理

RuntimeException

  • 屬於Unchecked Exception
  • 編成上的錯誤 EX:分母為零 傳入參數型態不符等......
  • 不需要自行throw此Exception
  • 不需自行處理但若RuntimeException及其子類別沒有處理則會一直往外丟最後由JVM來處理。 img/002.JPG

Exception

  • 屬於Checked Exception
  • 執行時預期之外的情況 EX:欲開啟的檔案不存在
  • RuntimeException 之外的Exception的子類別都屬於此類
  • 需要由programmer自行進行檢驗及處理
  • Java 強迫一定要處理的,如不處理,則 Compile 時期就會發生錯誤訊息

##Throw

public void f() throws Exception{
		System.out.println("Throw Exception from f()");
		throw new Exception();
	}

關鍵字throw會引發相關的動作。如果程式執行於某個函式內,而且會擲出異常 一般而言會先使用new產生一個用以標示錯誤狀態的物件, 然後再將所產生的reference交給throw 而被擲出。一般如果不進行處理,在擲出後便會脫離函式 有點像是return 但兩者的相似處只有這一點

Tryblock

在異常被擲出後必須被終止於某處,該處便是所謂的exception handler

try {
	被監督是否有例外發生的程式碼;
} catch (例外類別 例外變數) {
	例外處理程式碼;
} catch (例外類別 例外變數) {
	例外處理程式碼;
} catch (例外類別 例外變數) {
	例外處理程式碼;
} …
finally {
	善後處理程式碼;
	(比如:已開啟的檔案關閉)	
}

其中眾多catch區塊只有第一個捕捉到例外的區塊會被執行。其餘則不會被執行。 而finally區塊無論如何都一定會被執行

import java.util.zip.DataFormatException;

class a extends Exception{}
class b extends ClassNotFoundException{}
class c extends CloneNotSupportedException{}
class d extends DataFormatException{}


public class Trysomething {

	public void f() throws a{
		System.out.println("Throw a from f()");
		throw new a();	
	}
	public void g() throws b{
		System.out.println("Throw b from g()");
		throw new b();
	}	
	public void h() throws c{
		System.out.println("Throw c from h()");
		throw new c();
	}	
	public void j() throws d{
		System.out.println("Throw d from j()");
		throw new d();
	}	
	
	
	public static void main(String[] args) {
		
		Trysomething tryy = new Trysomething();
		
		try{
			tryy.g();
			tryy.h();
			tryy.j();
		} catch(b e) {
			System.out.println("get b from g()");
		} catch(c e) {
			System.out.println("get c from h()");
		} catch(d e) {
			System.out.println("get d from j()");
		}finally{
			System.out.println("This will always been print out");
		}
		
	}

}

##建造自己的 exceptions 我們並不會受限於Java所提供的exceptions,Java無法預測我們會需要的錯誤類型,所以我們可以撰寫自己的exceptions ,要撰寫自己的exceptions一定得繼承某個既有的exceptions。

class MyException extends Exception{  //最簡單的方法是讓編譯器產生default建構式
	public MyException(){}
	public MyException(String msg){
		super(msg);   //明確的指定呼叫"擁有String 引數"的base class建構式
	}
}

public class ExceptionsWithString {

		public void f() throws MyException{
			System.out.println("Throw MyException from f()");
			throw new MyException();			
		}
		
		public void g() throws MyException{
			System.out.println("Throw MyException from g()");
			throw new MyException("There is a problem");
		}
		
		public static void main(String[] args) {
			ExceptionsWithString sed = new ExceptionsWithString();
			
			try{
				sed.f();
								
			}catch(MyException e){
				e.printStackTrace(System.out);   //用來追蹤錯誤訊息的路徑
			}
			
			try{
				sed.g();
			}catch(MyException e){
				e.printStackTrace(System.out);
			}
		}
}

##Catch all the exceptions 在try{}catch(){} 的格式中 在最後一個catch之後額外增加一個 catch(Exception e){},可以捕捉到你原先在設計程式時所沒有料想到的exceptions

public class CatchAll {
	
		static int a;
		static int b;
		static int c;
	public static void main(String[] args) {
		
			a=100;
		try
		{ 
			System.out.println("Enter a div number");
			b=0;
			c=a/b; 
		}
		catch(NullPointerException e)
		{
			System.out.println("The number "+b);
		} 
		catch(Exception e)
		{
			System.out.println("Something else happened");
		} 
		finally
		{
		}
		
	}

}

##重擲異常 將異常重新擲出,會使該異常移動到下一個更高層的exception handler

public class ReThrowing {
	
	public static void f() throws Exception{
		System.out.println("originating the exception in f()");
		throw new Exception("thrown from f()");
	}
	public static void g() throws Exception{
		try{
			f();
		}catch(Exception e){
			System.out.println("Inside g() ,e.printStackTrace");
			e.printStackTrace(System.out);
			throw e;
		}
	}
	
	public static void h() throws Exception{
		try{
			f();
		}catch(Exception e){
			System.out.println("Inside h(),e.printStackTrace()");
			e.printStackTrace(System.out);
			throw (Exception)e.fillInStackTrace();  //將當前的Stack的資訊填入舊的異常物件並以Throwable回傳
								//只會trace到重新擲出點
		}
	}
	public static void main(String[] args) {
		try{
			g();
		}catch(Exception e){
			System.out.println("main:printStackTrace()");
			e.printStackTrace(System.out);
		}
		try{
			h();
		}catch(Exception e){
			System.out.println("main:printStackTrace()");
			e.printStackTrace(System.out);
		}
	}

}

##Checked Exception的爭議

例外處理的本意是,在程式錯誤發生時,能夠有明確的方式通知API客戶端,讓客戶端採取進一步的動作修正錯誤,Java是唯一採用Checked exception的語言,這有兩個目的:一是文件化,受檢例外宣告會是API操作介面的一部份,客戶端只要查閱文件,就可以知道方法可能會引發哪些例外,並事先加以處理,而這是API設計者決定是否拋出Checked exception時的考量之一,另一個目的是提供編譯器資訊, 讓編譯器能夠在編譯時期就檢查出API客戶端沒有處理例外。

問題在於有些錯誤發生而引發例外時,你是無力回復的,例如SQLException是受檢例外,而如果例外的發生原因是資料庫連線異常,而連線異常的原因是由於實體線路問題,那麼無論如何你都不可能使用try、catch回復到正常可運作的情況。 QLException發生時,最好的方法是將例外浮現至呈現層,例如網頁技術,將錯誤訊息於網頁上顯示出來給管理人員,為了讓例外往上浮現,你也許會選擇在每個方法呼叫上都宣告throws SQLException,但假設,這個方法的呼叫是在整個應用程式的底層, 這樣的作法也許會造成許多程式碼的修改

Checked exception的本意是好的,有助於程式設計人員注意到例外的可能性並加以處理,但在應用程式規模增大時,會對逐漸對維護造成困難

TODO

  • todo1
  • todo2

About

Thinking in Java CH 12

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages