Java學習12:異常
概念
程式在執行時出現不正常的情況。
異常由來
問題也是現實生活中的一個具體的事物,也可以通過java類的形式進行描述,並且封裝成物件,其實就是java對不正常情況進行描述後的物件體現。
對於問題的劃分有兩種:嚴重的,java通過Error類進行描述,一般不編寫針對性的程式碼對其進行處理;不嚴重的,通過Exception類進行描述,可以使用針對性的處理方式進行處理。
Throwable
|–Error
|–Exception
異常處理
try
{
需要被檢測的程式碼;
}
catch(異常類 變數)
{
處理異常的程式碼;(處理方式)
}
finally
{
一定會執行的語句;
}
/*
div 0!
/ by zero
java.lang.ArithmeticException: / by zero
java.lang.ArithmeticException: / by zero
at Function.div(Demo.java:178)
at Demo.main(Demo.java:187)
over
*/
class Function
{
double div(int a, int b)
{
return a / b;//(1) new AritchmeticException()
}
}
class Demo
{
public static void main(String[] args)
{
Function f = new Function();
try {
double x = f.div(1, 0);//(2) (1)傳過來的new AritchmeticException()
System.out.println("x = " + x);
}
catch(Exception e)//(3) Exception e = new AritchmeticException();
{
System.out.println("div 0!" );
System.out.println(e.getMessage());
System.out.println(e.toString());// 異常名稱:異常資訊
e.printStackTrace();// 異常名稱,異常資訊,異常出現的位置 其實JVM預設的異常處理機制,就是在呼叫printStackTrace方法,列印異常的堆疊的跟蹤資訊。
}
System.out.println("over");
}
}
對捕獲到的異常物件進行常見方法操作
String getMessage() 返回此throwable的詳細訊息字串
String toString() 返回此throwable的簡短表現形式
void printStackTrace() 將此throwable及其追蹤輸出至標準錯誤流
throws
在函式上通過throws的關鍵字聲明瞭該功能有可能會出現問題。便於提高安全性,讓呼叫者進行處理(捕獲或丟擲),不處理編譯失敗。
class Function
{
double div(int a, int b) throws Exception
{
return a / b;
}
}
class Demo
{
public static void main(String[] args) //throws Exception
{
Function f = new Function();
try {//不捕獲編譯會報錯(或者在主函式後面丟擲異常) Error:(187, 29) java: 未報告的異常錯誤java.lang.Exception; 必須對其進行捕獲或宣告以便丟擲
double x = f.div(1, 0);
System.out.println("x = " + x);
}
catch(Exception e)
{
System.out.println(e.toString());
}
System.out.println("over");
}
}
對多異常的處理
- 宣告異常時,建議宣告為更具體的異常,這樣處理的可以更具體。
- 對方宣告幾個異常, 就對應有幾個catch塊。如果多個catch中的異常出現繼承關係,父類異常catch塊放在最下面。
- 在進行catch處理時,catch中一定要定義具體處理方式,不要簡單定義一句e.printStackTrace(),也不要簡單的書寫一條輸出語句。
class Function
{
double div(int a, int b) throws ArithmeticException, ArrayIndexOutOfBoundsException
{
int[] x = new int[a];
System.out.println(x[4]);
return a / b;
}
}
class Demo
{
public static void main(String[] args) //throws Exception 拋給虛擬機器
{
Function f = new Function();
try {
double x = f.div(5, 0);
System.out.println("x = " + x);
}
catch(ArithmeticException e)//java.lang.ArithmeticException: / by zero
{
System.out.println(e.toString());
System.out.println("div 0!");
}
catch(ArrayIndexOutOfBoundsException e)//java.lang.ArrayIndexOutOfBoundsException: 4
{
System.out.println(e.toString());
System.out.println("陣列越界!");
}
System.out.println("over");
}
}
自定義異常
因為專案中會出現特有的問題,這些問題並沒有被java所描述並封裝物件,所以這些特有的問題可以按照java的對問題封裝的思想,將特有的問題進行自定義的異常封裝。
需求:本程式中,對於除數為負數,也視為是錯誤的,是無法進行運算的,那麼僅需要對此問題進行自定義的描述。
/*
FuShuException
div FuShu!
over
*/
class FuShuException extends Exception//自定義異常
{}
class Function
{
double div(int a, int b) throws ArithmeticException, FuShuException
{
if(b < 0)
throw new FuShuException();
return a / b;
}
}
class Demo
{
public static void main(String[] args)
{
try
{
double result = new Function().div(1, -1);
System.out.println("result = " + result);
}
catch(ArithmeticException e)
{
System.out.println(e.toString());
System.out.println("div 0!");
}
catch(FuShuException e)
{
System.out.println(e.toString());
System.out.println("div FuShu!");
}
System.out.println("over");
}
}
上述程式中的列印結果中只有異常的名稱,卻沒有異常的資訊,因為自定義的異常中未定義資訊。如何定義異常資訊?
因為父類中已經把異常資訊的操作都完成了,所以子類只要在構造時,將異常資訊通過super語句傳遞給父類,那麼就可以直接通過getMessage方法獲取自定義的異常資訊(toString呼叫getMessage方法)。下面程式中還包含了傳遞特有資訊。
/*
FuShuException: / by fushu
div FuShu!
illegal negative value is : -1
over
*/
class FuShuException extends Exception//自定義異常
{
private int value;
FuShuException(String msg, int value)
{
super(msg);
this.value = value;
}
int getValue()
{
return value;
}
// private String msg;
// FuShuException(String msg)
// {
// this.msg = msg;
// }
// public String getMessage()
// {
// return msg;
// }
}
class Function
{
double div(int a, int b) throws ArithmeticException, FuShuException
{
if(b < 0)
throw new FuShuException("/ by fushu", b);
return a / b;
}
}
class Demo
{
public static void main(String[] args)
{
try
{
double result = new Function().div(1, -1);
System.out.println("result = " + result);
}
catch(ArithmeticException e)
{
System.out.println(e.toString());
System.out.println("div 0!");
}
catch(FuShuException e)
{
System.out.println(e.toString());
System.out.println("div FuShu!");
System.out.println("illegal negative value is : " + e.getValue());
}
System.out.println("over");
}
}
注意:自定義類必須是繼承Exception,繼承Exception的原因:
異常體系有一個特點,因為異常類和異常物件都被丟擲,他們都具有可拋性,這個可拋性Throwable這個體系中獨有特點,只有這個體系中的類和物件才可以被throws和throw操作。
throws和throw的區別
throws使用在函式上,throw使用在函式內。
throws後面跟的異常類,可以跟多個,用逗號隔開。throw後面跟的是異常物件。
RuntimeException
- Exception中有一個特殊的子類異常RuntimeException(執行時異常)。
- 如果在函式內中丟擲該異常,函式上可以不用宣告,編譯一樣通過(其他異常編譯不通過);
class Function
{
double div(int a, int b)
{
if(b == 0)
//throw new Exception("div zero!"); 就會報錯"未報告的異常錯誤java.lang.Exception; 必須對其進行捕獲或宣告以便丟擲"
throw new ArithmeticException("div zero!");
return a / b;
}
}
class Demo
{
public static void main(String[] args)
{
Function f = new Function();
f.div(1, 0);
System.out.println("over!");
}
}
: 如果在函式上聲明瞭該異常,呼叫者可以不用進行處理,編譯一樣通過。
class Function
{
double div(int a, int b) throws ArithmeticException//如果是throws Exception,則會報錯“未報告的異常錯誤java.lang.Exception; 必須對其進行捕獲或宣告以便丟擲”
{
return a / b;
}
}
class Demo
{
public static void main(String[] args)
{
Function f = new Function();
f.div(1, 0);
System.out.println("over!");
}
}
之所以不用在函式宣告,是因為不需要讓呼叫者處理,當該異常發生,希望程式停止(我的理解就是這個異常太嚴重,已經無法處理),因為在執行時出現了無法繼續運算的情況,希望停止程式後,對程式碼進行修正。
所以,自定義異常時,如果該異常的發生,無法再繼續進行運算,就讓自定義異常繼承RuntimeException。
//Exception in thread "main" FuShuException: div Fushu!
class FuShuException extends RuntimeException
{
FuShuException(String msg)
{
super(msg);
}
}
class Function
{
double div(int a, int b)//throws FuShuException
{
if(b < 0)
//FuShuException繼承了RuntimeException,所以不用宣告異常,主函式不需要處理
throw new FuShuException("div Fushu!");
return a / b;
}
}
class Demo
{
public static void main(String[] args)
{
new Function().div(1, -2);
}
}
異常分為兩種
- 編譯時被檢測的異常;
- 編譯時不被檢測的異常(執行時異常,RuntimeException及其子類)
練習
class BlueScreenException extends Exception
{
BlueScreenException(String msg)
{
super(msg);
}
}
class SmokeException extends Exception
{
SmokeException(String msg)
{
super(msg);
}
}
class NoPlanException extends Exception
{
NoPlanException(String msg)
{
super(msg);
}
}
class Computer
{
private int status = 2;
public void run() throws BlueScreenException, SmokeException
{
if(status == 2)
throw new BlueScreenException("電腦藍屏!");
else if(status == 3)
throw new SmokeException("電腦冒煙!");
System.out.println("電腦執行!");
}
public void reset()
{
System.out.println("電腦重啟!");
}
}
class Teacher
{
public void test()
{
System.out.println("練習!");
}
public void prelect() throws NoPlanException
{
Computer comp = new Computer();
try {
comp.run();
}
catch(BlueScreenException e)
{
System.out.println(e.toString());
comp.reset();
}
catch(SmokeException e)
{
test();
throw new NoPlanException("課程無法繼續," + e.getMessage());
}
System.out.println("講課!");
}
}
class Demo
{
public static void main(String[] args)
{
Teacher t = new Teacher();
try {
t.prelect();
}
catch (NoPlanException e)
{
System.out.println(e.toString());
System.out.println("更換電腦");
}
}
}
finally程式碼塊
定義一定執行的程式碼,通常用於關閉資源。(在資料庫操作中將關閉資料庫放在這裡面)
三種格式
//1
try
{
}
catch()
{
//2
try
{
}
catch()
{
}
finally
{
}
//3
try
{
}
finally
{
}
注意: catch是用於處理異常,如果沒有catch就代表沒有被處理過,如果該異常是檢測時異常,那麼必須宣告,見下面程式。
class Demo
{
public void method()
{
//1.編譯不能通過 解決方法:(1)宣告異常throws Exception;(2)捕獲異常
throw new Exception();
//2.編譯能通過,捕獲異常
try
{
throw new Exception();
}
catch (Exception e)
{
}
//3.編譯不能通過,異常未被宣告或捕獲
try
{
throw new Exception();
}
catch (Exception e)
{
throw e;
}
//4.編譯能通過,捕獲異常
try
{
throw new Exception();
}
catch (Exception e)
{
try
{
throw e;
}
catch(Exception e2)
{
}
}
//5.編譯不能通過,異常未被宣告或捕獲
try
{
throw new Exception();
}
finally
{
//關閉資源
}
}
}
異常在子父類覆蓋中的體現
- 子類在覆蓋父類時,如果父類的方法丟擲異常,那麼子類的覆蓋方法,只能丟擲父類的異常或者該異常的子類;
- 如果父類方法丟擲多個異常,那麼子類在覆蓋該方法時,只能丟擲父類異常的子集;
- 如果父類或介面的方法中沒有異常丟擲,那麼子類在覆蓋方法時,也不可以丟擲異常,如果子類方法發生了異常,就必須要進行try處理,絕對不能拋。
異常例子
class InvalidValueException extends RuntimeException
{
InvalidValueException(String msg)
{
super(msg);
}
}
interface Shape//介面類
{
public abstract double getArea();
}
class Rec implements Shape//長方形
{
private double len, width;
Rec(double len, double width)
{
this.len = len;
this.width = width;
}
public double getArea()
{
if(len <= 0 || width <= 0)
throw new InvalidValueException("輸入非法值!");
return len * width;
}
}
class Circle implements Shape
{
double r;
private static final double PI = 3.14;//因為PI是共享資料,所以加static
Circle(double r)
{
this.r = r;
}
public double getArea()
{
if(r <= 0)
throw new InvalidValueException("出現非法值");
return r * r * PI;
}
}
class Demo
{
public static void main(String[] args)
{
Rec rec = new Rec(10, 2);
System.out.println(rec.getArea());
Circle circle = new Circle(-11);
System.out.println(circle.getArea());
}
}
總結
異常是對問題的描述,將問題進行物件的封裝。
異常體系:
Throwable
|–Error
|–Exception
異常體系特點:異常體系中所有類以及建立的物件都具有可拋性,也就是說可以被throw和throws關鍵字所操作,只有異常體系具備這個特點,
throw和throws的用法:
throw定義在函式內,用於丟擲異常物件;
throws定義在函式上,用於丟擲異常類,可以丟擲多個,用逗號隔開。
當函式內容有throw丟擲異常物件,並未進行try處理,必須要在函式上宣告,否則編譯失敗。
注意:RuntimeException除外,也就是說,函式內如果丟擲RuntimeException異常,函式上可以不用宣告。如果函式聲明瞭異常,呼叫者需要進行處理,處理方式可以是throws/try。
異常有兩種:
編譯時被檢測異常,該異常如果沒有處理,沒有throws也沒有try,編譯失敗,該異常被標識,代表可以被處理;
執行時異常(編譯時不檢測),在編譯時不需要處理,編譯器不檢查,該異常發生時,建議不處理,讓程式停止,需要對程式碼進行修正。
異常處理語句
try
{
需要被檢測的程式碼;
}
catch()
{
處理異常的程式碼;
}
finally
{
一定會執行的程式碼;
}
注意:
(1)finally中定義的通常是關閉資原始碼,因為資源必須釋放;
(2)finally只有一種情況不會執行,當catch中寫到System.exit(0)時系統退出,jvm結束,此時finally裡面的語句不執行。
自定義類異常
按照java的面向物件思想, 將程式中出現的特有問題進行封裝,定義類繼承Exception或者RuntimeException。
(1)為了讓自定義類具備可拋性;
(2)讓該類具備操作異常的共性方法。
當要定義自定義異常的資訊時,可以使用父類已經定義好的功能,異常資訊傳遞給父類的建構函式。
class MyException extends Exception
{
MyException(String msg)
{
super(msg);
}
}
異常的好處
(1)將問題進行封裝;
(2)將正常流程程式碼和問題處理程式碼相分離,方便於閱讀。
異常的處理原則
(1)處理方式:try或者throws;
(2)呼叫到丟擲異常的功能時,丟擲幾個,就處理幾個,一個try對應多個catch;
(3)多個catch,父類的catch放到最下面;
(4)catch內,需要定義針對性的處理方式,不要簡單地定義printStackTrace輸出語句,也不要不寫;
(5)當捕獲到的異常,本功能處理不了時,可以繼續在catch中丟擲;
try
{
cicle.getArea();
}
catch(InvalidValueException e)
{
throw e;
}
(6)如果該異常處理不了,但並不屬於該功能出現的異常, 可以將異常轉換後,再丟擲和功能相關的異常;或者異常可以處理,當需要將異常產生的和本功能相關的問題提供出去,讓呼叫者知道並處理,也可以將捕獲異常處理後轉換為新的異常;
//1
try
{
cicle.getArea();
}
catch(InvalidValueException e)
{
throw new BException();
}
//2
try
{
cicle.getArea();
}
catch(InvalidValueException e)
{
//對e進行處理,之後丟擲新的異常
throw new BException();
}
異常在子父類覆蓋中的體現