異常
1.1 異常概述
異常(Exception)指程式執行中出現的不正常情況:檔案找不到、網路異常、非法引數等等。
我們透過程式碼來了解一下:
public class Demo{
public static void main(String[] args){
int[] a={1,2,3};
method(a);
}
public static void method(int[] a){
Sytem.out.println(a[3]);
}
}
//輸出結果
ArrayIndexOutOfBoundsException//陣列下標越界
上面我們得到異常中一個非常常見的異常:陣列下標越界異常。
1.1.1 異常體系
Exception也是一個類,throwable是所有異常的超類;throwable還有一個子類是錯誤Error。
Error:表示很嚴重的問題,無需處理。
Exception:稱為異常類,表示程式本身可以處理的問題。
- RuntimeException:在編譯期是不檢查的,出現問題後需要我們會來修改程式碼;(也就是說我們在寫程式碼的過程中程式是不會報錯的)
- 非RuntimeException:在編譯期就必須處理的問題,如果不處理,將不能透過編譯,程式更加不會執行;(也就是寫程式碼爆紅的時候)
1.2 JVM的預設處理方案
當我們的程式出現異常的時候,如果我們不去處理,Java中的虛擬機器會執行預設處理方案。
public class demo{
public static void main(String[] args){
int[] a={1,2,3};
System.out.println("開始");
method(a);
System.out.println("結束");
}
public static void method(int[] a){
System.out.println(a[3]);
}
}
//輸出結果
開始
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 3//異常出現的型別
at com.liuwei.oop.exception.demo.method(demo.java:11) //異常出現的位置
at com.liuwei.oop.exception.demo.main(demo.java:7)
//結束這個輸出語句並沒有執行
如果我們不處理異常,交給系統去處理會得到異常的原因、異常的位置輸出在控制檯,且終止程式執行!
1.3 異常處理
我們為什麼需要處理異常呢?
因為交給Java預設處理的話,會終止我們的程式執行!如果我們自己將異常處理了,可以讓程式保持執行。
處理異常的方案:
- try—catch—
- throw
1.3.1 try—catch—
格式:
try{
可能出現異常的程式碼;
}catch(異常的型別 變數名){
如與catch中的異常型別匹配成功後需要執行的異常處理程式碼;
}
執行流程:
程式執行try內部的程式碼,判斷是否有異常,
若出現異常則將異常例項化
將異常的型別與catch中的異常型別進行匹配
匹配成功後,執行異常處理程式碼
程式執行後續程式碼.....
我們去程式碼中學習一下try—catch—的使用:
public class Demo{
public static void main(String[] args){
int[] a={1,2,3};
System.out.println("開始");
try{
method(a);//需要檢查異常的程式碼
//有異常是會例項化異常並與catch中的異常型別進行匹配,匹配成功執行異常處理程式碼
}catch(ArrayIndexOutOfBoundsException e){
System.out.println("陣列下標訪問錯誤");//異常的處理程式碼
}
System.out.println("結束");
}
public static void method(int[] a){
System.out.println(a[3]);
}
}
//輸出結果
開始
陣列下標訪問錯誤
結束
透過上面的程式碼執行,我們看到我們捕獲異常且處理後,後續的程式碼依然被執行了;
這也就是我們處理異常的初衷:出現異常後不影響後面程式碼的執行。
但是有需要我們注意的地方是異常處理程式碼一般來說不是輸出一句話,而是要顯示異常原因及位置的資訊;
我們在捕獲異常時例項化了物件,我們可以在異常處理程式碼的地方呼叫異常的一些方法來顯示我們想要的異常原因及位置的資訊。
public class Demo{
public static void main(String[] args){
int[] a={1,2,3};
System.out.println("開始");
try{
method(a);
}catch(ArrayIndexOutOfBoundsException e){
// System.out.println("陣列下標訪問錯誤");//異常的處理程式碼
e.printStackTrace();//我們呼叫了異常的方法,將錯誤資訊列印出來
}
System.out.println("結束");
}
public static void method(int[] a){
System.out.println(a[3]);
}
}
//輸出結果
開始
結束
java.lang.ArrayIndexOutOfBoundsException: 3
at com.liuwei.oop.exception.demo.method(demo.java:16)
at com.liuwei.oop.exception.demo.main(demo.java:8)
我們發現我們的異常處理得到的跟Java異常預設處理方案是一樣的,但是僅僅是顯示的內容一樣,而我們的程式並沒有終止。
1.4 Throwable的成員方法
成員方法 | 說明 |
---|---|
public String getMessage() | 返回此throwable的詳細訊息字串 |
public String toString() | 返回此可丟擲的簡短描述 |
public void printStackTrace() | 把異常的錯誤資訊輸出在控制檯 |
我們在程式碼裡對三種方法進行了解:
- public String getMessage()
public class Demo{
public static void main(String[] args){
int[] a={1,2,3};
System.out.println("開始");
try{
method(a);
}catch(ArrayIndexOutOfBoundsException e){
System.out.println(e.getMessage());
}
System.out.println("結束");
}
public static void method(int[] a){
System.out.println(a[3]);
}
}
//輸出結果
開始
3//getMessage();會返回出現異常的原因
結束
我們開啟getMessage()的原始碼:
發現getMessage();是Throwable類的一個方法;且getMessage();中返回了一個detailMessage
detailMessage是Throwable類的一個成員變數
也就是說我們呼叫getMessage();時返回給我們的是detailMessage,而detailMessage是Throwable類的屬性,也就意味著detailMessage在Throwable類的有參構造中被賦值了,而這個值正是其子類自身呼叫構造器時傳來的;
當try捕捉到異常時會建立該異常的物件,此時該異常類會呼叫有參構造器,且呼叫其父類的有參構造器,其父類再去呼叫父類,最終調到Throwable類的有參構造器對detailMessage進行賦值,最後我們呼叫getMessage();並返回detailMessage的值。
//簡化描述下Throwable有參的構造和getMessage()方法的呼叫
public class Throwable{
private String detailMessage;
public Throwable(String message){//當異常類的有參構造調動牽動了Throwable的有參構造器的呼叫,且將 detailMessage初始化了
detailMessage=message;
}
public String getMessage(){
return detailMessage;
}
}
- public String toString()
public class Demo{
public static void main(String[] args){
int[] a={1,2,3};
System.out.println("開始");
try{
method(a);
}catch(ArrayIndexOutOfBoundsException e){
System.out.println(e.toString());
}
System.out.println("結束");
}
public static void method(int[] a){
System.out.println(a[3]);
}
}
//輸出結果
開始
java.lang.ArrayIndexOutOfBoundsException: 3//輸出了異常型別以及原因;包含了getMessage();的內容
結束
- public void printStackTrace()
public class Demo{
public static void main(String[] args){
int[] a={1,2,3};
System.out.println("開始");
try{
method(a);
}catch(ArrayIndexOutOfBoundsException e){
e.printStackTrace();
}
System.out.println("結束");
}
public static void method(int[] a){
System.out.println(a[3]);
}
}
//輸出結果
開始
結束
java.lang.ArrayIndexOutOfBoundsException: 3
at com.liuwei.oop.exception.demo.method(demo.java:16)
at com.liuwei.oop.exception.demo.main(demo.java:8)
//輸出了異常的原因,型別,位置
//printStackTrace();輸出的資訊是最全的,一般我們使用這個
1.5 編譯時異常和執行時異常的區別
Java中的異常有兩大類:編譯時異常和執行時異常;也被稱為受檢異常和非受檢異常;
所有的RuntimeException及其子類都被成為執行時異常,除外其他的異常都為編譯時異常
- 編譯時異常:必須顯示處理,否則程式會發生錯誤,編譯無法透過;
- 執行時異常:無需顯示處理,需要我們修改程式碼;也可以和編譯時異常 一樣處理
public class Demo{
public static void main(String[] args){
int[] a={1,2,3};
method(a);//ArrayIndexOutOfBoundsException,需要我們修改程式碼
}
public static void method(int[] a){
System.out.println(a[3]);
}
}
類似我們上面的程式碼在編譯時沒有報錯,而執行時報錯,就是執行時異常。
編譯時異常怎麼處理?
也就是我們前面講的try catch
1.6 異常處理之throws
我們前面已經知道了try catch方式來處理異常,但是並不是所有的異常情況我們都有許可權透過try catch方式來處理;
Java提供了另外一種處理方式:throws處理方案
格式:
throw 異常類名;
需要注意的是throws 異常類名;是跟在方法宣告的括號之後的!
throws 異常類名僅僅是拋給了呼叫者,並沒有解決異常,如果要解決還得用try catch
//執行時異常的throws處理
public class Demo{
public static void main(String[] args){
//method();//此時throws將異常拋到此處,我們異常並沒有解決,我們還是需要處理異常,即用try catch方式來處理
try{
method();
}catch(ArrayIndexOutOfBoundsException e){
e.printStackTrace();
}
}
public static void method1() throws ArrayIndexOutOfBoundsException{//throws僅僅是向呼叫者丟擲異常,並不能解決異常
int[] a={1,2,3};//我們是可以透過try catch來處理的,這裡演示throws處理
System.out.println(a[3]);
}
}
總結:
- 編譯時異常必須進行處理:兩種處理方案:try catch 和 throws, throws要注意誰呼叫誰就要處理異常
- 執行時異常可以不處理:我們始終要回來修改程式碼