概述 對異常的理解 程式在運行過程中出現不正常情況。是對問題的描述,將問題進行對象的封裝。 異常的由來 問題也是現實生活中一個具體的事物,也可以通過Java的類的形式進行描述,並封裝成對象。 對於問題的劃分 一種是嚴重的問題,一種是非嚴重的問題。 + 對於嚴重的:Java通過Error類進行描述。 ...
概述
對異常的理解
程式在運行過程中出現不正常情況。是對問題的描述,將問題進行對象的封裝。
異常的由來
問題也是現實生活中一個具體的事物,也可以通過Java的類的形式進行描述,並封裝成對象。
對於問題的劃分
一種是嚴重的問題,一種是非嚴重的問題。
- 對於嚴重的:Java通過Error類進行描述。
對於Error一般不編寫針對性的代碼對其進行處理。 對於非嚴重的:Java通過Exception類進行描述。
對於Exception可以使用針對性的處理方式進行處理。
無論Error還是Exception都具有一些共性內容。
比如:不正常情況的信息,引發原因。異常體系
Throwable
|--Error
|--Exception
|--RuntimeException
異常體系的特點:
異常體系中的所有類以及被建立的對象都具備可拋性。
異常的處理
代碼語句格式
try {
需要被檢測的代碼
}
catch(異常類 變數) {
處理異常的代碼(處理方式)
}
finally {
一定會執行的語句;
}
對異常的常見操作方法
String getMessage();
String toString();
void printStackTrace();
class Demo {
int div(int x,int y) {
return x/y;
}
}
public class ExceptionDemo {
public static void main(String[] args) {
Demo d = new Demo();
try {
int z = d.div(1,0);
System.out.println(z);
}
catch (Exception e) { //Exception e = new ArithmeticExceptin();
System.out.println(e.getMessage()); // /by zero
System.out.println(e.toString()); // 異常名稱:異常信息
e.printStackTrace(); // 異常名稱:異常信息,異常出現的位置
//(jvm預設的處理異常的機制就是在調用printStackTrace()方法)
}
System.out.println("over");
}
}
運行結果
/ by zero
java.lang.ArithmeticException: / by zero
java.lang.ArithmeticException: / by zero
at Demo.div(ExceptionDemo.java:3)
at ExceptionDemo.main(ExceptionDemo.java:10)
over
異常聲明throws
- throws關鍵字用來聲明一個方法有可能會出現問題。
- throws關鍵字可以將異常拋給調用者,可以層層向上拋,直到最後由Java虛擬機拋出。
比如:
class Demo {
int div(int x,int y) throws Exception {
return x/y;
}
}
public class ExceptionDemo1 {
public static void main(String[] args) throws Exception {
Demo d = new Demo();
int y = d.div(1,0);
System.out.println(y);
System.out.println("over");
}
}
- 如果調用者對被調用方法拋出的異常不加處理會出現編譯錯誤。
class Demo {
int div(int x,int y) throws Exception {
return x/y;
}
}
public class ExceptionDemo2 {
public static void main(String[] args) {
Demo d = new Demo();
int y = d.div(1,0);
System.out.println(y);
System.out.println("over");
}
}
上述代碼編譯會提示:
Error:(9, 22) java: 未報告的異常錯誤java.lang.Exception; 必須對其進行捕獲或聲明以便拋出
我們對可能出現的異常加以處理:
class Demo {
int div(int x,int y) throws Exception {
return x/y;
}
}
public class ExceptionDemo3 {
public static void main(String[] args) {
Demo d = new Demo();
try {
int y = d.div(1,0);
System.out.println(y);
}
catch (Exception e) {
System.out.println(e.toString());
}
System.out.println("over");
}
}
運行結果:
java.lang.ArithmeticException: / by zero
over
對可能出現異常的div方法進行處理之後,程式可以正常編譯運行。
多異常處理
1.聲明異常時,聲明為更為具體的異常,這樣可以處理得更具體。
2.方法聲明有幾個異常,對應就有幾個catch塊。(不要定義多餘的catch塊)
如果多個catch塊中的異常出現繼承關係,父類異常catch塊放在最下麵。
class Demo {
int div(int x,int y) throws ArithmeticException,ArrayIndexOutOfBoundsException {
int[] arr = new int[x];
System.out.println(arr[3]);
return x/y;
}
}
public class ExceptionDemo4 {
public static void main(String[] args) {
Demo d = new Demo();
try {
int y = d.div(2,0);
}
catch (ArithmeticException e) {
System.out.println("被零除了");
}
catch (ArrayIndexOutOfBoundsException e) {
System.out.println("數組角標越界");
}
}
}
運行結果:
數組角標越界
上述代碼中顯然有兩個一場出現,但是當方法中出現一個異常之後方法會停止運行,不會繼續執行下去。
自定義異常
項目中會出現特有的問題,而這些問題並未被Java描述並封裝對象。所以對這些特有的問題可以按照Java的對問題封裝的思想,將特有的問題,進行自定義的異常封裝。
需求:在本程式中,對於除數是負數,也視為是錯誤的。
當在函數內部出現了throw拋出異常對象,那麼就必須要給對應的處理。
要麼在內部使用try catch處理。
要麼在函數上聲明讓調用者處理。
class FuShuException extends Exception {
}
class Demo {
int div(int x,int y) throws FuShuException{
if(y<0) {
throw new FuShuException();//通過throw關鍵字手動拋出一個自定義異常對象
}
return x/y;
}
}
public class ExceptionDemo5 {
public static void main(String[] args) {
Demo d = new Demo();
try {
int y = d.div(1,-1);
System.out.println(y);
}
catch (FuShuException e) {
System.out.println(e.toString());
System.out.println("除數出現負數了");
}
}
}
運行結果
FuShuException
除數出現負數了
我們可以發現以上的代碼運行的結果中只有異常的名稱,卻沒有異常的信息。因為我們並未在自定義的異常中定義具體信息。
定義異常信息
因為父類中已經把異常信息的操作完成了。所以子類只要在構造時,將異常信息傳遞給父類通過super語句,就可以通過getMessage方法獲取自定義的異常信息。
class FuShuException extends Exception {
private int value;
FuShuException(){
super();
}
FuShuException(String message,int value) {
super(message);
this.value = value;
}
public int getValue() {
return value;
}
}
class Demo {
int div(int x,int y) throws FuShuException{
if(y<0) {
throw new FuShuException("--被零除了--",y);//通過throw關鍵字手動拋出一個自定義異常對象
}
return x/y;
}
}
public class ExceptionDemo5 {
public static void main(String[] args) {
Demo d = new Demo();
try {
int y = d.div(1,-1);
System.out.println(y);
}
catch (FuShuException e) {
System.out.println(e.toString());
System.out.println("除數出現了負數:"+e.getValue());
}
}
}
運行結果:
FuShuException: --被零除了--
除數出現了負數:-1
註意
自定義異常必須是是自定義類繼承Exception
異常體系中的異常類和異常對象都具備可拋性,這個可拋性是Throwable這個體系中獨有的特點。只有這個體系中的類和對象才可以被throw和throws操作。
throw和throws的區別
- throws使用在函數上,throw使用在函數內。
- throws後面跟的是異常類,可以跟多個用逗號隔開。throw後跟的是異常對象。
RuntimeException
異常分兩種
- 編譯時被檢測的異常。
- 編譯時不被檢測的異常(運行時的異常。RuntimeException以及其子類)
RuntimeException是Exception中的一個特殊的子類。
如果在函數內容中拋出異常,函數上可以不用聲明。
如果在函數上聲明瞭異常,調用者可以不用進行處理。
之所以不用在函數上聲明是因為不需要讓調用者處理,當該異常發生,程式希望停止,對代碼進行修正,而不是去讓調用者處理。
自定義異常時,如果該異常發生,無法再繼續進行運算,就讓自定義異常繼承RuntimeException。
class FuShuException extends RuntimeException{
FuShuException(String message) {
super(message);
}
}
class Demo {
int div(int x,int y) { //函數上為聲明異常也可以正常編譯
if (y < 0) {
throw new FuShuException("--除數為負數--");//在函數內容中拋出異常
}
else if(y == 0){
throw new ArithmeticException("--除數為零--");//在函數內容中拋出異常
}
return x/y;
}
}
public class ExceptionDemo6 {
public static void main(String[] args) {
Demo d = new Demo();
try {
int y = d.div(1,-1);
}
catch (FuShuException e) {
System.out.println(e.toString());
}
catch (ArithmeticException e) {
System.out.println(e.toString());
}
}
}
finally
try {
}
catch () {
}
finally {
}
finally中存放的是一定會被執行(無論是否有異常)的代碼,即使catch中有return語句。
finally只有一種情況不會被執行,當代碼執行到System.exit(0);
例如:如果需要對資料庫進行操作,無論有沒有成功連接上資料庫,在最後操作完成的時候,都需要斷開資料庫的連接,以釋放有限的資源。
異常處理語句的另一種格式
try {
}
finally {
}
異常在覆蓋時的特點
- 子類在覆蓋父類時,如果父類的方法拋出異常,那麼子類的覆蓋方法只能拋出父類的異常或者該異常的子類。子類程式在繼承時不能拋出新的異常。
- 如果父類方法拋出多個異常,那麼子類在覆蓋方法時,只能拋出父類異常的子集。
- 如果父類或者介面的方法中沒有異常拋出,那麼子類在覆蓋方法時,也不可以拋出異常如果子類方法發生了異常,就必須要進行try處理,不可以拋出。
異常的好處
- 將問題進行封裝。
將正常流程代碼和問題處理代碼相分離,增強代碼可讀性。
異常的處理原則
- 兩種處理方式: try和throws
- 調用到拋出異常的功能時,拋出幾個,就需要處理幾個。一個try對應多個catch
- 多個catch需要將父類異常的catch放到最下麵。
- catch內,需要定義針對性處理方式,不要簡單地定義printStackTrace。
- 當捕獲到地異常,無法處理,可以繼續在catch中拋出該無法處理的異常
- 如果該異常處理不了,但並不屬於該功能出現的異常,可以將該異常進行轉換然後再拋出
- 如果異常可以處理,但需要使拋出的異常和本功能相關,也可以對異常進行轉換
異常練習
有圓形和長方形,都可以獲取面積,對於面積,如果出現非法的數值,視為是獲取面積出現問題。
出現的問題通過異常來表示。(使用異常可以,使問題處理代碼和正常流程代碼分開)
interface Shape {
void getArea();
}
class InvaidValueException extends RuntimeException {//如果一個傳入一個圖形的參數有錯誤,我們無法對其進行有效的處理,只拋出異常即可,所以繼承RuntimeException
InvaidValueException(String message) {
super(message);
}
}
class Rec implements Shape {
private double len,wid;
Rec(double len,double wid) {
if(len <= 0 || wid <= 0)
throw new InvaidValueException("非法值");
this.len = len;
this.wid = wid;
}
@Override
public void getArea() {
System.out.println(wid*len);
}
}
class Circle implements Shape {
private double r;
public static final double PI = 3.14;
Circle(double r) {
if(r <= 0)
throw new InvaidValueException("非法值");
this.r = r;
}
@Override
public void getArea() {
System.out.println(r*r*PI);
}
}
public class ExceptionDemo7 {
public static void main(String[] args) {
Rec R = new Rec(1,2);
R.getArea();
Circle C = new Circle(-1);
C.getArea();
System.out.println("over");
}
}
上述程式的運行結果為:
2.0
Exception in thread "main" InvaidValueException: 非法值
at Circle.<init>(ExceptionDemo7.java:28)
at ExceptionDemo7.main(ExceptionDemo7.java:41)