Java之Exception

Exception這個東西,程序中必須會有的,儘管咱們很不樂意看到它,但是從另外一個角度考慮,有異常則說明程序有問題,有助於咱們及時改正。有的時候程序出錯的緣由有不少,好比不合法的輸入、類型、空指針甚至內存不足,若是光從軟件來看,咱們只知道它出問題了,並不清楚問題出在哪兒,給軟件排錯是個很頭疼的事情,由於可能出問題的地方太多了,語法上的問題還好點兒,畢竟能從視覺上看出來,有些邏輯上的問題纔是致命的,咱們必須從全局出發也許才能找到問題的根源!java

轉自:http://blog.csdn.net/zhangerqing數組

1、簡介

Java爲咱們提供了很是完美的異常處理機制,使得咱們能夠更加專心的去寫程序,有的時候遇到須要添加異常處理塊的地方,像eclipse會自動提示你,感受很幸福!咱們看看異常處理的一些類的結構組成:eclipse

從根部開始分爲兩大類:Error和Exception。Error是程序沒法處理的錯誤,好比OutOfMemoryError、ThreadDeath等。這些異常發生時,Java虛擬機(JVM)通常會選擇線程終止。Exception是程序自己能夠處理的異常,這種異常分兩大類:非運行時異常(發生在編譯階段,又稱checkException)和運行時異常(發生在程序運行過程當中,又叫uncheckException)。非運行時異常通常就是指一些沒有遵照Java語言規範的代碼,容易看的出來,而且容易解決的異常,運行時異常是那些在程序運行過程當中產生的異常,具備不肯定性,如空指針異常等,形成空指針的緣由不少,因此運行時異常具備不肯定性,每每難以排查,還有就是程序中存在的邏輯錯誤,光從一段代碼中看不出問題,須要縱觀全局才能發現的錯誤,也會形成運行時異常,這就要求咱們在寫程序時多多注意,儘可能處理去處理異常,當異常發生時,但願程序能朝理想的方面運行!工具

2、異常的類型

一方面咱們能夠將異常分爲受控異常和不受控異常,其實通常來說,受控異常就是非運行時異常,不受控異常就是運行時異常和Error。另外一方面,咱們直接將異常分爲非運行時異常和運行時異常。性能

3、異常處理的過程

使用try/catch/finally語句塊安裝異常處理程序,每一個try塊中包含可能出現異常的語句,每一個catch塊中包含處理異常的程序,測試

public class Test {
 
	public static void main(String[] args) {
		String filename = "d:\\test.txt";
		try {
			FileReader reader = new FileReader(filename);
			Scanner in = new Scanner(reader);
			String input = in.next();
			int value = Integer.parseInt(input);
			System.out.println(value);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} finally {
			System.out.println("this is finally block!");
		}
	}
}

  

若是d盤根目錄下沒有test.txt的話,該程序拋出異常:this

this is finally block!
java.io.FileNotFoundException: d:\test.txt (系統找不到指定的文件。)
 at java.io.FileInputStream.open(Native Method)
 at java.io.FileInputStream.<init>(FileInputStream.java:106)
 at java.io.FileInputStream.<init>(FileInputStream.java:66)
 at java.io.FileReader.<init>(FileReader.java:41)
 at Test.main(Test.java:10)spa

可是finally塊中的語句卻輸出了,這個暫且不談,先記着,在d盤下新建文件test.txt,並輸入內容2232,再來觀察下:.net

輸出:線程

2322
this is finally block!

finally塊中的語句依然輸出,說明:不論程序有無異常,finally塊中的語句都會執行。所以finally塊中通常放一些關閉資源的語句。接下來咱們繼續作實驗,咱們將test.txt中的2322改爲abc,看看結果:

this is finally block!
Exception in thread "main" java.lang.NumberFormatException: For input string: "abc"
 at java.lang.NumberFormatException.forInputString(NumberFormatException.java:48)
 at java.lang.Integer.parseInt(Integer.java:447)
 at java.lang.Integer.parseInt(Integer.java:497)
 at Test.main(Test.java:13)
該異常中的兩處重點我已經標出來了,一處是紅色的Exception in thread 「main」,代表異常拋出的地方,另外一處是java.lang.NumberFormatException: For input string: "abc",代表異常的類型,此處咱們看看上面以前的那個結果,爲何沒有拋出異常出現的地方,仔細觀察源程序,咱們發現,程序中咱們並無顯式聲明NumberFormatException,而FileNotFoundException是咱們聲明過的,此處我總結一下就是說:一、若是我在程序中聲明瞭某個異常,則拋出異常的時候,不會顯示出處,直接拋出。二、若是我沒有在程序中聲明,那麼程序會同時拋出異常的出處。這是爲何?還有,當我沒有顯式聲明的時候,系統會怎麼辦?這確定是有必定的規律的,下面咱們繼續作實驗:

public class Test {
 
	public static void main(String[] args) {
 
		String filename = "d:\\test.txt";
 
		// 進行捕捉異常
		try {
			FileReader reader = new FileReader(filename);
			Scanner in = new Scanner(reader);
			String input = in.next();
			int value = Integer.parseInt(input);
			System.out.println(value);
		} catch (FileNotFoundException e) { // 捕捉FileNotFoundException
			e.printStackTrace();
		} catch (NumberFormatException e) { // NumberFormatException
			e.printStackTrace(); // 打印異常信息 就是形如:at java.lang.NumberFor...的信息
			System.out.println("I'm here!");
		} finally {
			System.out.println("this is finally block!");
		}
	}
}

 

我加了一個catch塊,轉麼捕獲NumberFormatException,則程序輸出:

java.lang.NumberFormatException: For input string: "abc"
 at java.lang.NumberFormatException.forInputString(NumberFormatException.java:48)
 at java.lang.Integer.parseInt(Integer.java:447)
 at java.lang.Integer.parseInt(Integer.java:497)
 at Test.main(Test.java:14)
I'm here!
this is finally block!

沒有輸出異常拋出的地方。繼續改代碼:

public class Test2 {
	
	public void open(){
		String filename = "d:\\test.txt";
		try {
			FileReader reader = new FileReader(filename);
			Scanner in = new Scanner(reader);
			String input = in.next();
			int value = Integer.parseInt(input);
			System.out.println(value);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			System.out.println("this is test2 block!");
		} 
	}
}

  

public class Test3 {
	
	public void carry() {
		Test2 t2 = new Test2();
		try {
			t2.open();
		} catch (Exception e) {
			e.printStackTrace();
			System.out.println("this is test3 block!");
		}
	}
}

  

public class Test {
 
	public static void main(String[] args) {
		
		Test3 t3 = new Test3();
 
		t3.carry();
	}
	
}

  

思路是:Test2類中處理業務,Test3類調用Test2類的open方法,最後在Test類中調用Test3類的carry方法,可是,我將異常拋在Test3中,看看異常輸出的結果:

java.lang.NumberFormatException: For input string: "abc"
 at java.lang.NumberFormatException.forInputString(NumberFormatException.java:48)
 at java.lang.Integer.parseInt(Integer.java:447)
 at java.lang.Integer.parseInt(Integer.java:497)
 at Test2.open(Test2.java:13)
 at Test3.carry(Test3.java:6)
 at Test.main(Test.java:7)
this is test3 block!

首先,拋出的異常沒有地方信息了,其次輸出了:this is test3 block!,說明該異常是從Test3類中的carry方法拋出的,當咱們把Test3類中的異常捕獲語句註釋掉的時候,異常以下:

Exception in thread "main" java.lang.NumberFormatException: For input string: "abc"
 at java.lang.NumberFormatException.forInputString(NumberFormatException.java:48)
 at java.lang.Integer.parseInt(Integer.java:447)
 at java.lang.Integer.parseInt(Integer.java:497)
 at Test2.open(Test2.java:13)
 at Test3.carry(Test3.java:6)
 at Test.main(Test.java:7)

看到此處,我想讀者朋友們應該有必定的感受了,說了這麼多,就是想說明一點,當程序處理不了異常的時候會怎麼辦?是這樣的:當前方法若是聲明瞭相應的異常處理器,如上面的程序若是加了catch(NumberFormatException e),則直接拋出,可是若是沒有聲明,則會找到它的調用者,若是調用者也沒有作相應的處理,則會一直往前找,直到找到main方法,最後拋出異常,因此上面的現象不難解釋!此處咱們簡單總結下異常處理的過程:一、在可能出錯的方法加上try/catch塊語句,來調用異常處理器。二、當異常發生時,直接跳到相應的異常處理器catch中,若是有則拋出異常,執行該catch塊中的語句,若是沒有,則找到它的調用者,直到main方法。三、若是有finally塊,則執行finally塊中的語句。

注意:

一、一個try可對應多個catch。二、有try必須至少有一個catch或者finally(此處經網友actt001指正,多謝!)。三、finally塊不是必須的,無關緊要。四、通常狀況下,當異常發生時,會執行catch塊中的語句,特殊狀況:當main方法中拋出異常時,若是程序聲明瞭該異常處理器,則執行相應的catch塊中的語句,若是程序沒有聲明相應的異常處理器,則不執行catch塊中的語句,直接拋出異常!那麼,這個異常來源於哪兒?既然main中有try/catch語句(雖然不是對應的異常處理器),爲何沒有拋出,說明main方法中的try/catch塊根本就沒有捕捉到異常,那麼系統怎麼處理?實際上是這樣的,這種狀況下,異常被直接丟給JVM,而JVM的處理方式就是:直接中斷你的程序!就是這麼簡單。

4、常見異常

NullPointerException 空指針

空指針異常。當應用試圖在要求使用對象的地方使用了null時,拋出該異常。譬如:調用null對象的實例方法、訪問null對象的屬性、計算null對象的長度、使用throw語句拋出null等等

ClassNotFoundException  找不到類

找不到類異常。當應用試圖根據字符串形式的類名構造類,而在遍歷CLASSPAH以後找不到對應名稱的class文件時,拋出該異常。

ClassCastException   類型轉換

ArithmeticException   算數條件

算術條件異常。譬如:整數除零等。

ArrayIndexOutOfBoundsException  數組越界

數組索引越界異常。當對數組的索引值爲負數或大於等於數組大小時拋出。

 

5、異常和錯誤

異常: 在Java中程序的錯誤主要是語法錯誤和語義錯誤,一個程序在編譯和運行時出現的錯誤咱們統一稱之爲異常,它是JVM(Java虛擬機)通知你的一種方式,經過這種方式,JVM讓你知道,你已經犯了個錯誤,如今有一個機會來修改它。Java中使用異常類來表示異常,不一樣的異常類表明了不一樣的異常。可是在Java中全部的異常都有一個基類,叫作Exception。

錯誤:它指的是一個合理的應用程序不能截獲的嚴重的問題,大多數都是反常的狀況,錯誤是JVM的一個故障(雖然它能夠是任何系統級的服務)。因此,錯誤是很難處理的,通常的開發人員是沒法處理這些錯誤的,好比內存溢出。

6、Assert(斷言)

assert是jdk1.4纔開始支持的新功能,主要在開發和測試時開啓,爲保證性能,在程序正式發佈後一般是關閉的。啓用斷言比較簡單,在啓動參數裏設置-ea或者-enableassertions就能夠了.

assert表達式有兩種狀況:

1)assert exp1 此時的exp1爲一個boolean類型的表達式

當其值爲true時,運行經過,若是爲false,則會拋出一個相應的AssertionError,注意它能夠被catch到。

 

2)assert exp1 : exp2 此時的exp1同上,而exp2能夠爲基本類型或一個Object對象,當exp1的值爲true時,同上,且exp2不會被運算;而當exp1的值爲false時,將會拋出AssertionError,同時將exp2的結果做爲AssertionError構造器中的參數,當使用catch該錯誤時,可利用getMessage()方法打印出exp2的結果。

使用斷言應該注意:斷言只是用來調試程序的工具,不要做爲程序的一部分,或者有人用斷言來代替try/catch,這些都是不對的,一、這和斷言的做用相違背,二、斷言在程序發佈後,是會被關閉的,若是將它做爲程序的一部分,那麼當斷言被關閉後,程序必然會出問題。三、有更好的方法,如try/catch,爲何還用斷言。因此,最好不要講斷言做爲程序的一部分,從內心上你能夠把它當作無關緊要就好了。

7、常見問題

一、finally和return問題

咱們平時說:finally中的內容不論程序有無異常,都會被執行,那麼若是咱們的程序在try和catch塊中return了,finally中的還會執行嗎?讀者能夠先猜猜看,分析一下,接下來咱們作實驗:

public class FinallyTest {
 
	public static void main(String[] args) {
		boolean file = open();
		System.out.println("this is main return value:" + file);
	}
 
	public static boolean open() {
		String filename = "d:\\test.txtp";
		try {
			FileReader reader = new FileReader(filename);
			Scanner in = new Scanner(reader);
			String input = in.next();
			int value = Integer.parseInt(input);
			System.out.println(value);
			return true;
 
		} catch (FileNotFoundException e) {
			System.out.println("this is catch_for_filenot... block!");
			return false;
		} finally {
			System.out.println("this is finally block!");
		}
	}
}

  

故意把filename寫錯,造出異常,輸出爲下:

this is catch_for_filenot... block!
this is finally block!
this is main return value:false

從這兒看出來,程序先輸出catch塊中的,後又去執行finally塊中的,雖然在catch中已經返回了,最後執行mian方法中的,並且輸出false,說明catch塊中的也成功返回了。因此,面對疑問,咱們能夠很確定的回答,即便有return語句,finally塊也必定會被執行!

二、儘可能不要將catch和finally一塊兒使用

像我上面演示程序那樣,try/catch/finally一塊兒使用,在《Big Java》一書中提到,不建議這樣作,由於會影響程序的可讀性,最好的作法是:用try/catch嵌套,catch用來捕獲異常,finally用來關閉資源,修改以下:

public class FinallyTest {
 
	public static void main(String[] args) {
		
		boolean file = open();
		System.out.println("this is main return value:" + file);
	}
 
	public static boolean open() {
		
		String filename = "d:\\test.txtp";
		try {
			try {
				FileReader reader = new FileReader(filename);
				Scanner in = new Scanner(reader);
				String input = in.next();
				int value = Integer.parseInt(input);
				System.out.println(value);
				return true;
 
			} finally {
				// 一些關閉資源的操做
				System.out.println("this is finally block!");
			}
 
		} catch (FileNotFoundException e) {
			System.out.println("this is catch_for_filenot... block!");
			return false;
		}
	}
}

  

三、自定義異常

畢竟系統自帶的異常處理器並不能知足全部需求,由於對於咱們開發人員來講,拋出的異常越細緻,咱們越容易找到問題,總不能全部的問題都拋出Exception吧?太籠統了。在實際的開發中,咱們能夠根據本身的須要,進行自定義異常處理器。

/**
 * 自定義異常處理器,繼承Exception或者RuntimeException,依狀況而定.
 * @author erqing
 *
 */
public class NameNotSupportException extends RuntimeException {
 
	private static final long serialVersionUID = 7295869280641332966L;
 
	public NameNotSupportException() {
	}
 
	public NameNotSupportException(String message) {
		super(message);
	}
}

  

public class DefineTest {
 
	public static void main(String[] args) {
		String name = "egg";
		if(!"erqing".equals(name)){
			throw new NameNotSupportException("erqing");
		}else{
			System.out.println("name is OK!");
		}
	}
}

  

Exception in thread "main" NameNotSupportException: erqing
	at DefineTest.main(DefineTest.java:7)
相關文章
相關標籤/搜索