Java異常的處理機制

圖片解析:

1.生成字節碼文件的過程可能產生編譯時異常(checked),由字節碼文件到在內存中加載、運行類此過程可能產生運行時異常(unchecked),

2.JAVA程序在執行過程中所發生的異常事件可分為兩類:

> Error: Java虛擬機無法解決的的嚴重問題。如:JVM系統內部錯誤、資源耗盡等嚴重情況。比如:StackOverflowError和OOM.一般不編寫針對性的代碼進行處理。

> Exception: 其他因編程錯誤或偶然的外在因素導致的一般性問題,可以使用針對性的代碼進行處理。例如:

空指針訪問、試圖讀取不存在的文件、網絡連接中斷、數組角標越界

捕獲錯誤最理想的實在編譯期間,但有的錯誤隻有在運行時才會發生。比如:除數為0,數組角標越界等

編譯時異常(執行javac.exe命令時,可能出現的異常):IOException、FileNotFoundException(IOException的子類)、 ClassNotFoundException

運行時異常(執行java.exe命令時,出現的異常):NullPointerException、ArrayIndexOutOfBoundsException、ArrayIndexOutOfBoundsException、ClassCastException、NumberFormatException、InputMismatchException、ArithmeticException

public class ExceptionTest{
    //NullPointerException
    @Test
    public void test1(){
        int[] arr = null;
        System.out.println(arr[3]);
        str = null;
        System.out.println(str.charAt(0));
    }
    //IndexOutOfBoundsException
    @Test
    public void test2(){
        //ArrayIndexOutOfBoundsException
        int[] arr = new int[10];
        System.out.println(arr[10]);
        //StringIndexOutOfBoundsException
        str = "abc";
        System.out.println(str.charAt(3));
    }
    //ClassCastException:類型不匹配
    @Test
    public void test3(){
        Object obj = new Date();
        String str = (String)obj;
    }
    //NumberFormatException:數據格式異常
    @Test
    public void test4(){
        str = "abc";
        int num = Integer.parseInt(str);
    }
    //InputMismatchException:輸入不匹配異常
    @Test
    public void test5(){
        Scanner scanner = new Scanner(System.in);
        int score = scanner.nextInt();
        System.out.println(score);
    }
    //ArithmeticException:算數異常
    @Test
    public void test6(){
        int a = 10;
        int b= 0;
        System.out.println(a\b);
    }
}

異常的處理:

在編寫程序時,經常要在可能出現錯誤的地方加上檢測的代碼,如進行x/y運算時,要檢測分母為0,數據為空,輸入的不是數據而是字符等。過多的if-case分支會導致程序的代碼加長,臃腫,可讀性差,因此采用異常處理機制。

Java采用的異常處理機制,是將異常處理的程序代碼集中在一起,與正常的程序代碼分開,使得程序 簡潔、優雅,並易於維護。

異常的處理:抓拋模型

過程一:”拋”:程序在正常執行的過程中,一旦出現異常,就會在異常代碼處生成一個對應異常類的對象,並將此對象拋出。一旦拋出以後,其後的代碼就不再執行。

關於異常對象的產生:A. 系統自動生成的異常對象

B. 手動的生成一個異常對象,並拋出(throw)

過程二:”抓”:可以理解為異常的處理方式:A.try-catch-finally B.throws

強調:過程一和過程二屬於配合的方式,是並列關系

處理機制一:try-catch-finally

try{
    //可能出現異常的代碼
}catch(異常類型1 變量名1){//一段代碼可能有多個異常類型
    //處理異常的方式1
}catch(異常類型2 變量名2){
    //處理異常的方式2
}catch(異常類型3 變量名3){
    //處理異常的方式3
}
.....
finally{
    //一定會執行的代碼
}

說明:

1.finally是可選的(可有,也可沒有,不會影響異常的處理)

2.使用try將可能出現異常代碼包裝起來,在執行過程中,一旦出現異常,就會生成一個對應異常類的對象,根據此對象的類型,去catch中進行匹配。

3.一旦try中的異常對象匹配到某一個catch時,就進入catch中進行異常的處理,一旦處理完成,就跳出當前的try-catch結構(在沒有寫finally的情況)。繼續執行其後的代碼。

4.catch中的異常類型如果沒有子父類的關系,則誰生命在上,誰聲明在下無所謂。

catch中的異常類型如果滿足子父類的關系,則要求子類一定聲明在父類的上面,否則,報錯

5.常用的異常對象處理的方式:

A.(返回值時String,可以用輸出語句查看)getMessage()

B.(開發中常用)printStackTrace()

6.在try結構中聲明的變量:再出瞭try結構以後,就不能在被調用

7.try-catch-finally結構可以嵌套

體會1:使用try-catch-finally處理編譯時異常,使得程序在編譯時就不再報錯,但是運行時仍可能報錯。相當於我們使用try-catch-finally將一個編譯時可能出現的異常,延遲到運行時出現(即把編譯時異常轉換為運行時異常)

體會2:開發中由於運行時異常比較常見,所以我們通常就不針對運行時異常編寫try-catch-finally瞭,針對編譯時異常,我們一定要考慮異常的處理。

    @Test
	public void test1() {
		str = "abc";
		int num = 0;// 聲明放在外面,出瞭try,還可以使用num;
        //但是此時要註意:在try-catch結構中,num不一定被賦值,所以要手動的給賦默認初始化值
		try {
			num = Integer.parseInt(str);
		} catch (NullPointerException e) {
			System.out.println(e.getMessage());
		} catch (NumberFormatException e) {
			e.printStackTrace();
		}
		System.out.println(num);
	}

finally的再說明:

1. finally是可選的

2.finally中聲明的是一定會被執行的代碼,即使catch中又出現異常瞭、try中有return語句、catch中有return語句等情況。

3.finally中一定會執行的結構在加載順序上優於try、catch中的異常代碼

@Test//catch中又出現異常瞭
public void test1(){
    try{
        int a = 10;
        int b = 0;
        System.out.println(a / b);
    }catch(ArithmeticException e){
        int[] arr = new int[10];
        System.out.println(arr[10]);
    }
        //System.out.println("catch中又出現異常瞭,但我一定不會輸出!");
    finally{
        System.out.println("catch中又出現異常瞭,但我一定會輸出!");
    }
}
@Test//try中有return語句、catch中有return語句
public void testMethod(){
    int num = method();
}
public int method(){
    try{
        int[] arr = new int[10];
        System.out.println("arr[10]");
        return 1;
    }catch(ArrayIndexOutOfBoundException e){
        e.printStockTrace();
        return 2;
    }finally{
        System.out.println("我一定會被執行!");
        return 3;
    }
}
//finally中一定會執行的結構在執行順序上優於try、catch中的return語句
//由於方法隻能有一個返回值,所以最後返回的是return 3;

3.像數據庫連接、輸入輸出流、網絡編程Socket等資源,JVM是不能自動的回收的,我們需要自己手動的進行資源的釋放。此時,就需要聲明在finally中。

處理機制二:throws + 異常類型

1. “throws + 異常類型”寫在方法的聲明處,指明此方法執行時,可能會拋出的異常類型。

一旦當方法體執行時,出現異常,仍會在異常代碼處生成一個異常類的對象,此對象滿足throws後異常類型時,就會被拋出。異常代碼後續的代碼,就不再執行!

2.體會:try-catch-finally:真正的將異常給處理掉瞭.(以後在其他方法中調用含有異常的方法時,不會報編譯時錯誤瞭)
throws的方式隻是將異常拋給瞭方法的調用者。並沒有真正將異常處理掉。(拋至main時,必須處理掉,不然拋給JVM,JVM就掛瞭)

public class ExceptionTest2 {
	public static void main(String[] args) {
		try {
			method2();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		method3();// 在method2()裡已經解決瞭異常,所以可以調用
	}
	public static void method3() {
		try {
			method2();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	public static void method2() throws FileNotFoundException, IOException {
		method1();
	}
	public static void method1() throws FileNotFoundException, IOException {//異常代碼
		File file = new File("hello.txt");
		FileInputStream fis = new FileInputStream(file);
		int data = fis.read();
		while (data != -1) {
			System.out.print((char) data);
			data = fis.read();
		}
		fis.close();
		System.out.println("hanhan");// 不會被執行!
	}
}

方法重寫的規則之一:
子類重寫的方法拋出的異常類型不大於父類被重寫的方法拋出的異常類型
意味著:如果父類中沒有出現異常,則子類中也不能有異常

我的理解:針對Java中的異常,如果時編譯時異常,則需要將其延遲為運行時異常,異常處理機制的作用也就是這樣,目的是為瞭給程序員一個提示,運行時異常的根本還是修改代碼。

手動拋出異常對象

public class StudentTest {
	public static void main(String[] args) {
		Student s = new Student();
		s.regist(-1001);
		System.out.println(s);
	}
}
class Student {
	private int id;
	public void regist(int id) {
		if (id > 0) {
			this.id = id;
		} else {//方式一:拋出運行時異常,運行時會報錯
			// 手動拋出異常對象
			throw new RuntimeException("您輸入的數據非法!");
		}
	}
}
public class StudentTest {
	public static void main(String[] args) {
		try {
			Student s = new Student();
			s.regist(-1001);
			System.out.println(s);
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
	}
}
class Student {
	private int id;
	public void regist(int id) throws Exception {//throws:體現異常的處理
		if (id > 0) {
			this.id = id;
		} else {//拋出編譯時異常:必須顯式的處理
			throw new Exception("您輸入的數據非法!");//throw:體現生成一個異常對象
		}
	}
}

開發中應該如何選擇兩種處理方式?

1. 如果父類中被重寫的方法沒throws方式處理異常,則子類重寫的方法也不能使用throws,意味著如果子類重寫的方法中異常,必須使用try-catch-finally方式處理。

2.執行的方法a中,先後有調用瞭另外的幾個方法,這幾個方法時遞進關系執行的,我們建議這幾個方法使用throws的方式進行處理,而執行的方法a可以考慮使用try-catch-finally方式進行處理。

用戶自定義異常類

如何自定義異常類?

1.繼承於現有的異常結構:RuntimeException、Exception
2.提供全局變量:serialVersionUID
3.提供重載的構造器

public class StudentTest {
	public static void main(String[] args) {
		try {
			Student s = new Student();
			s.regist(-1001);
			System.out.println(s);
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
	}
}
class Student {
	private int id;
	public void regist(int id) throws Exception {
		if (id > 0) {
			this.id = id;
		} else {
			throw new MyException("不能輸入負數");//自定義異常類的使用
		}
	}
}
public class MyException extends RuntimeException {//自定義異常類
	static final long serialVersionUID = -7034897190745766939L;
	public MyException() {
	}
	public MyException(String msg) {
		super(msg);
	}
}

throw和throws的區別:

throw:表示拋出一個異常類的對象,生成異常對象的過程,聲明在方法體內。

throws:屬於異常類處理的一種方式,聲明在方法的聲明處

到此這篇關於Java異常的處理機制的文章就介紹到這瞭,更多相關Java 異常內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: