1.定義:
異常:是在運行時期發生的不正常情況。
異常類:在java中用類的形式對不正常情況進行了描述和封裝對象。描述不正常的情況的類,就稱爲異常類
以前正常流程代碼和問題處理代碼相結合,
現在將正常流程代碼和問題處理代碼分離,提高閱讀性。
2.體系:
問題一多,類就越多。
將其共性向上抽取,形成體系:
Throwable: 父類,無論是error,還是異常,都是問題,問題發生就可以拋出,讓調用者知道並處理。
該體系的特點:就在於Throwable及其所有的子類都具有可拋性。子類的後綴名都是用其父類名作爲後綴,閱讀性很強。
可拋性的體現:通過兩個關鍵字來體現:thorws,throw。凡是可以被這兩個關鍵字所操作的類和對象都具有可拋性。
3.問題的分類:
最終問題就分成了兩大類:
1.一般不可處理。Error
特點:是由jvm拋出的嚴重性的問題。
這種問題發生一般不針對性處理。直接修改程序。
2.可以處理的。Exception
例子:
class ExceptionDemo1
{
public static void main(String[] args)
{
}
public static void sleep(int time)
{
if(time<0)
{
拋出 new FuTime(); //就代表着時間爲負的情況,這個對象中會包含着問題的名稱,信息,位置等信息。
}
System.out.println(time);
}
}
class FuTime //異常類
{
}
4.異常原理:
例子:
/*
異常原理
*/
class Demo
{
int method(int[] arr, int index)
{
/* //②可以自己產生異常
if(arr == null)
throw new NullPointerException("數組引用不能爲空");
if(index >= arr.length)
{
throw new ArrayIndexOutOfBoundsException("數組角標越界");
}
*/
return arr[index]; //①默認會進行異常對象封裝:throw new ArrayIndexOutOfBoundsException(index)
//程序終止,下面的代碼不再執行
}
}
class ExceptionDemo2
{
public static void main(String[] args)
{
int[] arr = new int[3];
Demo d = new Demo();
int num = d.method(null,3); //異常拋出到這裏,然後主函數又丟給虛擬機。虛擬機執行異常處理機制,把異常信息打印。
//可惜用戶看不懂
//程序終止,下面的代碼不再執行
System.out.println(num);
}
}
5.自定義異常: 聲明 throws
注意點:如果讓一個類稱爲異常類,必須要繼承異常體系,因爲只有成爲異常體系的子類,纔有資格具備可拋性。
才能被兩個關鍵字所操作,throws,throw
異常的分類:
1.編譯時被檢測異常:Exception和其子類都是,除了特殊子類RuntimeException體系。(需要異常聲明throws處理)
如:自定義異常FuShuIndexException
這種問題一旦出現,希望在編譯時就進行檢查,讓這種問題有對應的處理方式。
2.編譯時不檢查異常:Exception的特殊子類RuntimeException及其子類。(它們在編譯時不需要異常聲明處理)
如:數組越界異常ArrayIndexOutOfBoundsException,空指針異常NullPointerException等。(遇到較多)
這種問題的發生,無法讓功能繼續,運算無法進行,更多是因爲調用者的原因導致的或者引發了內部狀態的改變導致的。
那麼這種問題一般不處理,直接編譯通過,在運行時,讓調用者調用時的程序強制停止,讓調用者對代碼進行修正。
所以自定義異常時,要麼繼承Exception,要麼繼承RuntimeException。
throws 和throw的區別:
1.throws 使用在函數上
throw 使用在函數內
2.throws拋出的是異常類,可以拋出多個,用逗號隔開
throw 拋出的是異常對象。
例子:
/*
自定義異常:聲明 throws
舉例:對於負數角
標這種異常java中沒有定義過。
那麼,就按照java異常的創建思想,面向對象,將負數角標進行自定義描述,並封裝成異常類。
java先檢查語法錯誤,纔會檢查邏輯錯誤。異常最後報錯。
*/
class FuShuIndexException extends Exception
{
FuShuIndexException()
{
}
FuShuIndexException(String msg)
{
super(msg); //調用的父類的構造方法。
}
}
class Demo
{
int method (int[] arr, int index) throws FuShuIndexException //要在函數聲明後添加 異常聲明
{
if(index <0)
throw new FuShuIndexException("數組角標負數異常"); //錯誤:未報告的異常錯誤FuShuIndex; 必須對其進行 捕獲 或 聲明 以便拋
return arr[index];
}
}
class ExceptionDemo3
{
public static void main(String[] args) throws FuShuIndexException //添加 異常聲明
{
int[] arr = new int[3];
Demo d = new Demo();
int i = d.method(arr,-1); //使用了異常聲明函數,也必須在函數上添加 異常聲明
System.out.println(i);
}
}
6.異常自定義處理: 捕捉
這是可以對異常進行 針對性 處理方式。
具體格式:
try
{
//需要被檢測異常的代碼
}
catch(異常類 變量) //接收發生異常的對象
{
//處理異常的代碼
}
finally
{
//一定會被執行的代碼
}
這個問題如果自己不能處理,就用throws
如果自己可以處理,就用try
例子:
class FuShuIndexException extends Exception
{
FuShuIndexException()
{
}
FuShuIndexException(String msg)
{
super(msg);
}
}
class Demo
{
int method(int[] arr, int index) throws FuShuIndexException,NullPointerException //如果拋出了多個異常,則需要多catch
{
if(arr == null)
throw new NullPointerException();
if(index <0)
throw new FuShuIndexException("數組角標爲負數!");
return arr[index];
}
}
class ExceptionDemo4
{
public static void main(String[] args) //如果異常自己解決不了,就拋出去throws,給虛擬機執行.printStackTrace()。
{
int[] arr = new int[3];
Demo d = new Demo();
try
{
int i = d.method(null,0); //異常拋在這裏,new FuShuIndexException("數組角標爲負數!");
System.out.println(i);
}
catch (FuShuIndexException f) //接收拋來的異常對象,而不是直接給虛擬機了。
//FuShuIndexException f = new FuShuIndexException("數組角標爲負數!");
{
System.out.println(f.getMessage()); //可以使用異常對象,及其父類的所有方法,打印需要的異常信息
//輸出異常信息
System.out.println(f); //該對象不是類型和哈希值,(f.toString())異常對象有建立自己的獨特字符串類型(可以該的,其實是默認加了toString()方法)。
//輸出異常類名 + 異常信息
f.printStackTrace(); //輸出異常類名 + 異常信息 ,及其追蹤輸出。 注意返回值是空類型,不要直接打印。
//jvm默認的異常處理機制就是調用異常對象的這個方法。
System.out.println("負數角標異常!"); //自行處理方式
}
/* catch() //多異常多catch
{
}*/
catch(Exception e) //如果爲了檢測其他異常,父類的catch應該放最後,因爲是按順序檢測。而所有異常對象都屬於Exception。
//多catch時面試會問。
{
e.printStackTrace();
}
System.out.println("over"); //程序繼續運行
}
}
//通常建立日誌文件用第三方工具,如 log4j(log for java)
多catch情況:父類的catch應該放最後,因爲是按順序檢測。而所有異常對象都屬於Exception。
7.異常處理原則:
1.函數內部如果拋出需要檢測的異常,那麼函數上必須要聲明。
否者必須在函數內用trycatch捕捉,否者編譯失敗。
2.如果調用到了聲明異常的函數,要麼trycatch,要麼throws,否者編譯失敗。
3.功能內部可以解決,用catch.
解決不了,用throws告訴調用者,由調用者解決。
4.一個功能如果拋出了多個異常,那麼調用時,必須對應多個catch進行針對性的處理。
內部有幾個需要檢測的異常,就拋幾個異常。拋出幾個,就catch幾個。
8.finally代碼塊:
1)作用舉例:
連接數據庫。
查詢。 Exception
關閉連接。 一定要執行,放在finally代碼塊裏。
2)代碼塊組合:
1.try catch finally
2.try catch(對個) 當沒有必要資源需要釋放時,可以不用定義finally
3.try finally
沒catch就沒有處理,所以還是要在函數上throws聲明異常來處理。
用處:異常先不處理,但是資源得關掉。
void show() throws Exception
{
try
{
//開啓資源
throw new Exception();
//這裏無法關閉資源,因爲執行不到
}
finally
{
//關閉資源
}
}
3)例子:
class Demo
{
int show(int index)
{
if(index < 0)
throw new ArrayIndexOutOfBoundsException("越界了!");
int[] arr = new int[3];
return arr[index];
}
}
class ExceptionDemo5
{
public static void main(String[] args)
{
Demo d = new Demo();
try
{
int num = d.show(-3);
System.out.println(num);
}
catch (ArrayIndexOutOfBoundsException e)
{
System.out.println(e.toString());
return; //如果System.exit(0); 推出jvm,這時finally不執行
}
finally
{
System.out.println("finally"); //函數結束,也會執行
}
System.out.println("over"); //函數如果提前結束,不會執行
}
}
9.異常的應用:
/*
異常的應用: 異常轉換:不要有什麼異常就拋出什麼異常,要看調用者能否處理。
void addData(Data d) throws Exception //不拋出數據庫異常,因爲調用者不懂數據庫。
//進行異常轉換,拋出調用者可以處理的異常。
{
連接數據庫。
try
{
添加數據。出現異常 SQLException();
}
catch(SQLException e)
{
處理數據庫異常。
throws new Exception("數據沒有添加成功"); //可以自定義異常拋出
}
finally
{
關閉數據庫。
}
}
畢老師用電腦上課。
問題領域中涉及兩個對象。
畢老師,對象。
分析其中的問題:
比如電腦藍屏,冒煙了。
*/
class LanPingException extends Exception
{
LanPingException(String msg)
{
super(msg);
}
}
class MaoYanException extends Exception
{
MaoYanException(String msg)
{
super(msg);
}
}
class prelectStop extends Exception
{
prelectStop(String msg)
{
super(msg);
}
}
class Computer
{
private int state = 2;
void run() throws LanPingException,MaoYanException
{
if(state == 1)
throw new LanPingException("電腦藍屏了!");
if(state == 2)
throw new MaoYanException("電腦冒煙了!");
System.out.println("電腦運行");
}
void reset()
{
state = 0;
System.out.println("電腦重啓");
}
}
class Teacher
{
private String name;
private Computer comp;
Teacher(String name)
{
this.name = name;
comp = new Computer();
}
void prelect() throws prelectStop // throws MaoYanException //老師講課拋出電腦冒煙,不合理
{
try
{
comp.run();
System.out.println("講課");
}
catch (LanPingException e)
{
System.out.println(e.getMessage());
comp.reset(); //預處理,可以自己處理,所以才設置爲編譯時檢測異常Exception,而不是運行時異常RuntimeException
prelect();
}
catch (MaoYanException e)
{
System.out.println(e.getMessage());
test();
//可以對電腦進行維修,但是不傳給調用者,因爲他也處理不了
//throw e; //冒煙異常處理不了,返回給調用者在方法聲明中標識出來。
throw new prelectStop("課程進度無法完成!原因:" + e.getMessage()); //返回調用者可以處理的異常,異常進行了轉換
}
}
void test()
{
System.out.println("做練習");
}
}
class ExceptionDemo6
{
public static void main(String[] args)
{
Teacher t= new Teacher("畢老師");
try
{
t.prelect();
}
catch (prelectStop e)
{
System.out.println(e.getMessage());
System.out.println("換人");
}
}
}
10.異常的注意事項:
1.子類在覆蓋父類方法時,父類方法如果拋出了異常,那麼子類的方法只能拋出父類的異常或者該異常的子類,也可以不拋。
2.如果父類拋出多個異常,那麼子類只能拋出父類異常的子集。
簡單點說:如果父類的方法沒有拋出異常,那麼子類覆蓋時絕對不能拋。(爸爸夠壞了,兒子不能更壞了)
這時如果子類還是使用了異常,只能try,不能拋。
/*
原因:如果在方法2中傳入了父類的對象,並trycatch了父類的方法1,這時catch中是父類的異常類型。如果有一個子類覆蓋了父類的
方法1,這時如果方法2中傳的是子類的對象(向上轉型),則先調用的是子類中覆蓋的方法,這時catch中的異常類型只有是父類的異常
或者子類纔不會出現問題。
*/
//舉例:
class A extends Exception
{
}
class B extends A
{
}
class C extends Exception
{
}
class Fu
{
void show() throws A
{}
}
class Zi extends Fu
{
void show() throws B //這時應該爲A或者B
{}
}
class Test
{
void method(Fu f)
{
try
{
f.show();
}
catch(A a)
{
}
}
}
class ExceptionDemo7
{
public static void main(String[] args)
{
Test t = new Test();
// t.method(new Fu());
t.method(new Zi());
}
}