在編程中異常報錯是不可避免的。特別是在學習某個語言初期,看到異常報錯就抓耳撓腮,經常開玩笑說編程1分鐘,改bug1小時。今天就讓咱們來看看什麼是異常和怎麼合理的處理異常吧!java
下面仍是先讓咱們來看一下基本概念吧!程序員
異常指程序運行過程當中出現的非正常現象,例如用戶輸入錯誤、除數爲零、須要處理的文件不存在、數組下標越界等。異常機制本質就是當程序出現錯誤,程序安全退出的機制。在Java的異常處理機制中,引進了不少用來描述和處理異常的類,稱爲異常類。異常類定義中包含了該類異常的信息和對異常進行處理的方法。面試
Java是採用面向對象的方式來處理異常的。處理過程: 正則表達式
讓咱們來看看前面所講到的異常類到底是個什麼東西!數據庫
其實全部的異常對象都是派生於Throwable類的一個實例。若是內置的異常類不可以知足須要,還能夠建立本身的異常類。全部異常的根類爲java.lang.Throwable。看看它的家族長什麼樣。編程
Throwable類下面主要是兩大門派。Error和Exception。數組
Error是程序沒法處理的錯誤,表示運行應用程序中較嚴重問題,系統JVM已經處於不可恢復的崩潰狀態中。例如,說內存溢出和線程死鎖等系統問題。安全
Exception是程序自己可以處理的異常。架構
Exception類是全部異常類的父類,其子類對應了各類各樣可能出現的異常事件。 一般Java的異常可分爲:函數
下面咱們來研究研究這兩個異常。
首先咱們先來看看什麼是運行時異常。
這類異常一般是由編程錯誤致使的,因此在編寫程序時,並不要求必須使用異常處理機制來處理這類異常,而是常常須要經過增長「邏輯處理來避免這些異常」。
好比如下常見的幾種異常:
ArithmeticException異常
int b=0;
System.out.println(1/b);
//解決:
if(b!=0){
System.out.println(1/b);
}複製代碼
NumberFormatException異常
String str = "1234abcf";
System.out.println(Integer.parseInt(str));
//解決:
Pattern p = Pattern.compile("^\\d+$");
Matcher m = p.matcher(str);
if (m.matches()) { // 若是str匹配表明數字的正則表達式,纔會轉換
System.out.println(Integer.parseInt(str));
}複製代碼
ClassCastException異常
Animal a=new Dog();
Cat c=(Cat)a;
//解決:
if (a instanceof Cat) {
Cat c = (Cat) a;
}複製代碼
這裏再補充兩點,方便你們更好的理解java異常的機制和處理過程。
上面咱們講述了什麼是運行時異常以及一些處理方式,下面就再來看看什麼是已檢查異常吧!
全部不是RuntimeException的異常,統稱爲Checked Exception,又被稱爲「已檢查異常」,如IOException、SQLException等以及用戶自定義的Exception異常。 這類異常在編譯時就必須作出處理, 不然沒法經過編譯。
一般異常的處理方式有兩種:
使用「try/catch」捕獲異常
使用「throws」聲明異常。
下面就來詳細的聊聊吧!
上面已經提了,異常處理一般有2種方式。先看看捕獲異常吧。
捕獲異常是經過3個關鍵詞來實現的:try-catch-finally。用try來執行一段程序,若是出現異常,系統拋出一個異常,能夠經過它的類型來捕捉(catch)並處理它,最後一步是經過finally語句爲異常處理提供一個統一的出口,finally所指定的代碼都要被執行。
這個捕獲異常其實也是咱們在面試的時候會常常碰到的問題。下面咱們分別再來對各個部分作一個簡單的提示吧!
一個try語句必須帶有至少一個catch語句塊或一個finally語句塊 。當異常處理的代碼執行結束之後,不會再回到try語句去執行還沒有執行的代碼。
每一個try語句塊能夠伴隨一個或多個catch語句,用於處理可能產生的不一樣類型的異常對象。在此介紹一些經常使用的方法,這些方法均繼承自Throwable類 。
這裏有一個須要特別注意的地方,那就是catch捕獲異常時的捕獲順序:
若是異常類之間有繼承關係,在順序安排上就需注意。越是頂層的類,越放在下面,再否則就直接把多餘的catch省略掉。 也就是說先捕獲子類異常再捕獲父類異常。
finally語句塊中始終都要執行,除了遇到了System.exit(0)結束程序運行。針對這個特性,因此咱們一般在finally中關閉程序塊已打開的資源,好比:關閉文件流、釋放數據庫鏈接等。
即便try和catch塊中存在return語句,finally語句也會執行。是在執行完finally語句後再經過return退出。
在這裏就有一道很是經典的一個面試題。
public class Test {
public static void main(String[]args) {
System.out.println(new Test().test());;
}
static int test(){
int x = 1;
try{
retun x;
}finally{
System.out.print("jdbk"+ ++x);
}
}
}
// 問輸出結果?複製代碼
先解釋哈這裏存在的玄妙吧!
看了上面的講述,咱們都知道了當try和catch中有return時,finally仍然會執行,因此正常邏輯來講此題的答案應該是「jdbk2 2」,但這裏存在一個陷阱,那就是:
finally是在return後面的表達式運算後執行的(此時並無返回運算後的值,而是先把要返回的值保 存起來,無論finally中的代碼怎麼樣,返回的值都不會改變,任然是以前保存的值),因此函數返回值是 在finally執行前肯定的。所以正確答案應該是:「jdbk2 1」。
還有一點須要注意的就是:finally中最好不要包含return,不然程序會提早退出,返回值不是try或catch中保存的返回值。
接下來再來說講聲明異常吧,它相對來講就比較簡單了。
在一些狀況下,當前方法並不須要處理髮生的異常,而是向上傳遞給調用它的方法處理。若是一個方法拋出多個已檢查異常,就必須在方法的首部列出全部的異常,之間以逗號隔開。
public static void readFile(String fileName) throws FileNotFoundException,IOException {
}複製代碼
須要注意的地方就是:
咱們爲何要自定義異常?還不是由於在程序中,可能會遇到JDK提供的任何標準異常類都沒法充分描述清楚咱們想要表達的問題。此時咱們就能夠建立本身的異常類,即自定義異常類。
那咱們怎麼自定義異常類呢?相信你看了上面的異常類的家族圖應該就猜到了。不錯,自定義異常類只需從Exception類或者它的子類派生一個子類便可。若是你繼承Exception類,則爲受檢查異常,必須對其進行處理;若是不想處理,可讓自定義異常類繼承運行時異常RuntimeException類。一般咱們自定義異常類應該包含2個構造器:一個是默認的構造器,另外一個是帶有詳細信息的構造器。這裏舉一個例子。
/**IllegalAgeException:非法年齡異常,繼承Exception類*/
class IllegalAgeException extends Exception {
//默認構造器
public IllegalAgeException() {
}
//帶有詳細信息的構造器,信息存儲在message中
public IllegalAgeException(String message) {
super(message);
}
}
public void setAge(int age) throws IllegalAgeException {
if (age < 0) {
throw new IllegalAgeException("人的年齡不該該爲負數");
}
this.age = age;
}複製代碼
最後給你們講述一點使用異常機制的建議:
1.要避免使用異常處理代替錯誤處理,這樣會下降程序的清晰性,而且效率低下。
2.處理異常不能夠代替簡單測試---只在異常狀況下使用異常機制。
3.不要進行小粒度的異常處理---應該將整個任務包裝在一個try語句塊中。
4.異常每每在高層處理。
最後,最近不少小夥伴找我要Linux學習路線圖,因而我根據本身的經驗,利用業餘時間熬夜肝了一個月,整理了一份電子書。不管你是面試仍是自我提高,相信都會對你有幫助!目錄以下:
免費送給你們,只求你們金指給我點個贊!
也但願有小夥伴能加入我,把這份電子書作得更完美!
推薦閱讀: