異常
- 一、異常概述與異常體系結構
- 二、常見異常
- 三、異常處理機制一:try-catch-finally
- 四、異常處理機制二:throws
- 五、手動拋出異常:throw
- 六、用戶自定義異常類
- 七、開發中如何選擇使用try-catch-finally還是使用throws
- 八、如何看待代碼中的編譯時異常和運行時異常?
- 九、throw和throws區別
一、異常概述與異常體系結構
在使用計算機語言進行項目開發的過程中,即使程序員把代碼寫得盡善盡美,在系統的運行過程中仍然會遇到一些問題,因為很多問題不是靠代碼能夠避免的,比如:客戶輸入數據的格式,讀取文件是否存在,網絡是否始終保持通暢
等。
-
異常:在Java語言中,將程序執行中發生的不正常情況稱為“異常”。(開發過程中的語法錯誤和邏輯錯誤不是異常)
-
Java程序在執行過程中所發生的的異常事件可分為兩類:
- Error:Java虛擬機無法解決的嚴重問題。如:JVM系統內部錯誤、資源耗盡等嚴重情況。比如:
StackOverflowError和OOM(OutOfMemoryError)
。一般不編寫針對性的代碼進行處理。 - Exception:其他因編程錯誤或偶然的外在因素導致的一般性問題,可以使用針對性的代碼進行處理。例如:
- 空指針訪問
- 視圖讀取不存在的文件
- 網絡連接中斷
- 數組角標越界
- 舉例:
//1.棧溢出 Exception in thread "main" java.lang.StackOverflowErrormain(args);//2.堆溢出 Exception in thread "main" java.lang.OutOfMemoryError: Java heap spaceInteger[] arr= new Integer[1024*1024*1024];
-
對于這些錯誤,一般有兩種
解決方法
:一是遇到錯誤就終止程序的運行。另一種方法是由程序員在編寫程序時,就考慮到錯誤的檢測、錯誤消息的提示,以及錯誤的處理。 -
捕獲錯誤最理想的是在
編譯期間
,但有的錯誤只有運行時
才會發生。比如:除數為0,數組下標越界
等。- 分類:
編譯時異常
和運行時異常
- 分類:
- Error:Java虛擬機無法解決的嚴重問題。如:JVM系統內部錯誤、資源耗盡等嚴重情況。比如:
-
結構圖(面試題:常見的異常有哪些?)
二、常見異常
FileNotFoundException、IOExcpetion、ClassNotFoundException、ClassCastException、NullPointerException。
三、異常處理機制一:try-catch-finally
- 在編寫程序時,經常要在可能出現錯誤的地方加上檢測的代碼,如進行x/y運算時,要
檢測分母為0,數據為空,輸入的不是數據而是字符
等。過多的if-else分支會導致程序的代碼加長、臃腫,可讀性差。因此采用異常處理機制。 - Java異常處理
Java采用的異常處理機制,是將異常處理的程序代碼集中在一起,與正常的程序代碼分開,使得程序簡潔、優雅,并易于維護。 - try-catch-finally
(1)finally是可選的
(2)使用try將可能出現異常代碼包裝起來,在執行過程中,一旦出現異常,就會生成一個對應異常類的對象,根據此對象的類型去catch中進行匹配
(3)一旦try中的異常匹配到某一個catch時,就進入catch中進行異常的處理,一旦處理完成,
就跳出當前的try-catch結構(沒有寫finally的情況),繼續執行其后的代碼
(4)catch中的異常類型如果沒有子父類關系,則誰聲明在上,誰聲明在下無所謂。
catch中的異常類型滿足子父類的關系,則要求子類一定聲明在父類上面。否則報錯。
(5) 常用的異常對象處理的方式: one:String getMessage(); two:printStackTrace()
(6) 在try結構中聲明的變量,再出了try結構以后,就不能再被調用
(7) try-catch-finally結構可以嵌套。 - 示例:
import org.junit.Test;import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;/*** 一、異常的處理: 抓拋模型* 過程一: “拋”, 程序在正常執行過程中,一旦出現異常,就會在異常代碼處生成一個對應異常類的對象。并將此對象拋出。* 一旦拋出對象以后,其后的代碼就不再執行。** 關于異常對象的產生(1)系統自動生成的異常對象* (2)手動的生成一個異常對象,并拋出 throw** 過程二: “抓”, 可以理解為異常的處理方式 (1)try-catch-finally (2)throws**二、try-catch-finally* try{* // 可能出現異常的代碼* }catch(異常類型1 變量名1){* // 處理異常的方式1* }catch(異常類型2 變量名2){* // 處理異常的方式2* }* ...* finally{* // 一定會執行的代碼* }** 說明:* (1)finally是可選的* (2)使用try將可能出現異常代碼包裝起來,在執行過程中,一旦出現異常,就會生成一個對應異常類的對象,根據此對象的類型* 去catch中進行匹配* (3)一旦try中的異常匹配到某一個catch時,就進入catch中進行異常的處理,一旦處理完成,* 就跳出當前的try-catch結構(沒有寫finally的情況),繼續執行其后的代碼* (4)catch中的異常類型如果沒有子父類關系,則誰聲明在上,誰聲明在下無所謂。* catch中的異常類型滿足子父類的關系,則要求子類一定聲明在父類上面。否則報錯。* (5) 常用的異常對象處理的方式: one:String getMessage(); two:printStackTrace()* (6) 在try結構中聲明的變量,再出了try結構以后,就不能再被調用* (7) try-catch-finally結構可以嵌套。** 體會1:使用try-catch-finally處理編譯時異常,使得程序在編譯時就不再報錯,但是運行時仍可能報錯。* 相當于我們使用try-catch-finally將一個編譯時可能出現的異常,延遲到運行時出現。* 體會2:開發中,由于運行時異常比較常見,所以我們通常就不針對運行時異常進行編寫try-catch-finally了* 針對于編譯時異常,我們說一定要考慮異常的處理。*/
public class TryCatchTest {@Testpublic void demo1(){String str = "hello";try {Integer.parseInt(str);System.out.println("轉換結束...");}catch (NumberFormatException e){
// System.err.println("出現數值轉換異常: NumberFormatException");//String getMessage();String message = e.getMessage();System.out.println(message); //For input string: "hello"e.printStackTrace();/*java.lang.NumberFormatException: For input string: "hello"at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)at java.lang.Integer.parseInt(Integer.java:580)at java.lang.Integer.parseInt(Integer.java:615)at com.notes._1Java基礎編程._7異常處理._3異常的處理方式._3異常的處理方式.demo1(_3異常的處理方式.java:42)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.lang.reflect.Method.invoke(Method.java:498)at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)at org.junit.runners.ParentRunner.run(ParentRunner.java:363)at org.junit.runner.JUnitCore.run(JUnitCore.java:137)at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)*/}// System.out.println("執行結束...");}@Testpublic void demo2(){try {FileInputStream fis = new FileInputStream("E:\\附件\\file\\txt\\hello.txt");int index = ' ';while ((index = fis.read()) != -1){System.out.print((char)index);}fis.close();}catch (FileNotFoundException e){e.printStackTrace();}catch (IOException e){e.printStackTrace();}}
}
import org.junit.Test;import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;/*** try-catch-finally的使用* 1、finally是可選的* 2、finally中聲明的是一定會執行的代碼。即使catch中又出現異常了、try中有return語句、catch中有return語句等情況。* 3、像數據庫連接、輸入輸出流、網絡編程socket連接等資源,JVM是不能自動回收的,我們需要手動的進行資源的釋放。此時資源的釋放,就需要聲明在finally中。**/
public class FinallyTest {@Testpublic void demo1(){try {int a = 10 / 0;System.out.println(a);}catch (ArithmeticException e){e.printStackTrace();}catch (Exception e){e.printStackTrace();}finally {System.out.println("執行結束");}}//finally中聲明的是一定會執行的代碼。即使catch中又出現異常了、try中有return語句、catch中有return語句等情況。@Testpublic void demo2(){int a = method();System.out.println(a);}public int method(){try {int a = 10 / 0;System.out.println(a);return 1;}catch (ArithmeticException e){e.printStackTrace();return 2;}catch (Exception e){e.printStackTrace();return 3;}finally {System.out.println("執行結束");
// return 4;}}//像數據庫連接、輸入輸出流、網絡編程socket連接等資源,JVM是不能自動回收的,我們需要手動的進行資源的釋放。此時資源的釋放,就需要聲明在finally中。@Testpublic void demo3(){FileInputStream fis = null;try {fis = new FileInputStream("hello.txt");int index = ' ';while ((index = fis.read()) != -1){System.out.print((char)index);}}catch (FileNotFoundException e){e.printStackTrace();}catch (IOException e){e.printStackTrace();}finally {try {if (fis != null){ //可能會報空指針異常fis.close();}}catch (IOException e){e.printStackTrace();}}}
}
- finally
- finally是可選的
- finally中聲明的是一定會被執行的代碼。即使catch中又出現了異常了,try中有return語句,catch有return語句等情況。
- 像數據庫連接、輸入輸出流、網絡編程Socker等資源,JVM是不能自動的回收的,我們需要自己手動的進行資源的釋放。此時的資源釋放,就需要聲明在finally中。
- 示例代碼:(重點是return返回值問題)
public static void main(String[] args) {int returnValue = finallyReturnTest();System.out.println("returnValue = " + returnValue);}public static int finallyReturnTest() {int m = 0 ;try{m = 8/0;return m; }catch(ArithmeticException e) {return 1; //返回return 1} finally {System.out.println("測試finllay");//return 3;//如果打開注釋返回 return 3}//return m;}
四、異常處理機制二:throws
import org.junit.Test;import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;/*** 異常的處理方式二: throws + 異常類型** 1、"throws + 異常類型" 聲明在方法的聲明處。指明此方法執行時,可能會拋出的異常類型。* 一旦當方法體執行時,出現異常,仍會在異常代碼處生成一個異常類的對象,此對象滿足throws后異常類型時,就會被拋出。* 異常代碼后續的代碼,就不再執行。** 2、體會:try-catch-finally 真正的將異常給處理掉了* throws的方式只是將異常拋給了方法的調用者,并沒有真正將異常處理掉。** 3、開發中如何選擇使用try-catch-finally 還是使用throws* (1)如果父類被重寫的方法沒有用throws方式處理異常,則子類重寫的方法也不能使用throws,意味著如果子類重寫的方法中有異常,必須使用try-catch-finally方式處理。* (2)執行的方法a中,先后又調用了另外的幾個方法,這幾個方法是遞進關系執行的,我們建議這幾個方法使用throws的方式進行處理,而執行的方法a可以考慮使用try-catch-finally方式進行處理。*/
public class ThrowsTest {public void demo1() throws FileNotFoundException, IOException{FileInputStream fis = new FileInputStream("hello.txt");int index = ' ';while ((index = fis.read()) != -1){System.out.print((char)index);}fis.close();}public void demo2() throws IOException{demo1();}@Testpublic void demo3(){try {demo2();} catch (IOException e) {e.printStackTrace();}}
}
- 重寫方法聲明拋出異常的原則
- 子類重寫方法拋出異常的類型,不大于父類被重寫方法拋出異常的類型
- 如果子類重寫方法拋出異常的類型,大于父類被重寫方法拋出異常的類型,進行try-catch后,可能捕捉不到子類拋出的異常,程序會報錯終止
public class JavaDemoException {public static void main(String[] args) {Car car = new Bar();try {car.startCar();//編譯時 按照父類拋出的異常的進行編譯,所有子類重寫的方法拋出的異常不能大于父類拋出的異常} catch (FileNotFoundException e) {e.printStackTrace();}}
}class Car {//啟動車輛public void startCar() throws FileNotFoundException {}
}class Bar extends Car {public void startCar() throws IOException { //報錯}
}
五、手動拋出異常:throw
public class _5手動拋出異常對象 {public static void main(String[] args) {Student student = new Student();try {student.register(-1001);System.out.println(student);} catch (Exception e) {System.err.println(e.getMessage());}}
}class Student{private int id;public void register(int id) throws Exception {if (id > 0){this.id = id;}else {
// System.out.println("輸入的數據非法: " + id);//手動拋出異常對象
// throw new RuntimeException("輸入的數據非法 id: " + id); //運行時異常throw new Exception("輸入的數據非法 id: " + id);}}@Overridepublic String toString() {return "Student{" +"id=" + id +'}';}
}
-
Java異常類對象除在程序執行過程中出現異常時由系統自動生成并拋出,也可根據需要使用人工創建并拋出。
- 首先要生成異常類對象,然后通過throw語句實現拋出操作(提交給Java運行環境)。
IOException e = new IOException(); throw e;
- 可以拋出的異常必須是Throwable或其子類的實例。下面的語句在編譯時將會產生語法錯誤:
throw new String("want to throw");
六、用戶自定義異常類
- 自定義異常類的注意點
- 一般地,用戶自定義異常類都是RuntimeException的子類。
- 自定義異常類通常需要編寫幾個重載的構造器。
- 自定義異常需要提供serialVersionUID
- 自定義的異常通過throw拋出。
- 自定義異常最重要的是異常類的名字,當異常出現時,可以根據名字判斷異常類型。
- 用戶自定義異常類MyException,用于描述數據取值范圍錯誤信息。用戶自己的異常類必須繼承現有的異常類。
class MyException extends Exception {static final long serialVersionUID = 13465653435L;private int idnumber;public MyException(String message, int id) {super(message);this.idnumber = id;}public int getId() {return idnumber;}
}
public class MyExpTest {public void regist(int num) throws MyException {if (num < 0)throw new MyException("人數為負值,不合理", 3);elseSystem.out.println("登記人數" + num);}public void manager() {try {regist(100);} catch (MyException e) {System.out.print("登記失敗,出錯種類" + e.getId());}System.out.print("本次登記操作結束");}public static void main(String args[]) {MyExpTest t = new MyExpTest();t.manager();}
}
- 例題:
七、開發中如何選擇使用try-catch-finally還是使用throws
- 如果父類中被重寫的的方法沒有throws方式處理異常,則子類重寫的方法也不能使用throws,意味著如果子類重寫的方法中有異常,必須使用try-catch-finally方式處理
public static void main(String[] args) {Car car = new Bar();try {car.startCar();//編譯時 按照父類拋出的異常的進行編譯,所有子類重寫的方法拋出的異常不能大于父類拋出的異常} catch (FileNotFoundException e) {e.printStackTrace();}}
}class Car {//啟動車輛public void startCar() {}
}class Bar extends Car {public void startCar() throws FileNotFoundException { //報錯}
}
- 執行的方法a中,先后又調用了另外的幾個方法,這幾個方法是遞進關系執行的。我們建議這幾個方法使用throw的方法進行處理。而執行的方法a可以考慮使用try-catch-finally方式進行處理。
public class JavaDemoException {public static void main(String[] args) {try {methodA();} catch (RuntimeException e) {System.out.println(e.getMessage());}methodB();}static void methodA() {try {System.out.println("進入方法A");throw new RuntimeException("制造異常");} finally {System.out.println("用A的方法的finally");}}static void methodB() {try {System.out.println("進入方法B");return;} finally {System.out.println("用B的方法的finally");}}
/* 進入方法A用A的方法的finally制造異常進入方法B用B的方法的finally*/}
八、如何看待代碼中的編譯時異常和運行時異常?
- 體會1:使用try-catch-finally處理編譯時異常,是得程序在編譯時就不再報錯,但是運行時仍可能報錯。相當于我們使用try-catch-finally將一個編譯時可能出現得異常,延遲到運行時出現。
- 體會2:開發中,由于運行時異常比較常見,所以我們通常就不針對運行時異常編寫try-catch-finally了。
- 針對于編譯時異常,我們說一定要考慮異常的處理。
九、throw和throws區別
- throw表示拋出一個異常類的對象,生成異常對象的過程。聲明在方法體內。
- throws屬于異常處理的一種方式,聲明在方法的聲明處。