一、什麼是異常處理 異常處理從字面的意思來講就是一種發生在 java 程式中的錯誤並對其處理,但對程式員而言,異常的處理不單單的簡單去處理異常,就 ok 了,還有眾多的概念和使用異常的方式方法需要掌握 異常在 java 中分有三種: 1、編譯時異常(受檢異常) > 這種異常發生的概率很高; 2、運行 ...
一、什麼是異常處理
異常處理從字面的意思來講就是一種發生在 java 程式中的錯誤並對其處理,但對程式員而言,異常的處理不單單的簡單去處理異常,就 ok 了,還有眾多的概念和使用異常的方式方法需要掌握
異常在 java 中分有三種:
1、編譯時異常(受檢異常)------> 這種異常發生的概率很高;
2、運行時異常 ------> 這種異常發生的概率較低,發生時直接退出 JVM;
3、錯誤(error)-----> 錯誤和異常是不同,錯誤其實是脫離了程式員控制的問題,錯誤在代碼中經常被忽略。比如記憶體溢出,在編譯過程也是發現不了的;
如上圖可以看出受檢異常與運行時異常都繼承於 Exception 類, 而 Exception 與 Error 繼承 Throwable 類,最終它們的父類都是 Object;
二、處理異常的方式有哪些
處理異常的方式有兩種:
1、throws <上拋>:在需要進行異常處理的方法體上加上 <throws 異常列表>,該方法體的異常將不會被本方法進行處理,如果誰調用該方法,則調用該方法的同時將異常一起調用。當然調用該方法的調用者可以進行處理該異常,也可以同樣使用拋出異常。
這樣的操作其實只是將異常交給了其他的程式處理,如果其他程式沒有能力進行處理,則將一直進行拋出,直到遇見一個能夠處理該異常的程式,並終止異常(該如何處理該異常呢?);
通過一個程式來更深次的瞭解 throws 如何處理異常:
1 1 import java.io.FileInputStream; 2 2 import java.io.FileNotFoundException; 3 3 4 4 /** 5 5 * throws 的深層次理解 6 6 */ 7 7 public class Test17 { 8 8 9 9 // public static void main(String[] args) throws FileNotFoundException{ // 當然這裡也可以直接使用父類 Exception 10 10 11 11 public static void main(String[] args) throws Exception{ 12 12 13 13 System.out.println("我是入口"); 14 14 15 15 m1(); 16 16 } 17 17 18 18 public static void m1() throws FileNotFoundException{ 19 19 // 從m1 - m3 的調用可以看出 throws 處理機制是不對異常處理,只是拋出異常,誰調用該異常,誰處理,最終會流向哪裡處理呢? 20 20 21 21 System.out.println("m1...start!"); 22 22 m2(); // m1() 也同樣出現了異常 23 23 System.out.println("m1...end!"); 24 24 } 25 25 26 26 public static void m2() throws FileNotFoundException{ 27 27 // 28 28 29 29 System.out.println("m2...start!"); 30 30 m3(); // 從這個地方我們就可以看出了當 m2()進行調用 m3()時,m2() 方法體也出現需要處理的異常 31 31 // 同樣的可以依照 m3() 方法進行處理 32 32 33 33 System.out.println("m2...end!"); 34 34 } 35 35 36 36 // public static void m3() throws NullPointerException{ 37 37 // 已經對該方法體進行了拋出異常的設置,為什麼是無效的? 38 38 // NullPointerException 因為這個是一個空指針的異常,它不對 IO 流的異常進行處理 39 39 40 40 public static void m3() throws FileNotFoundException { 41 41 // FileNotFoundException 異常機制對 m3 起到了效果,主要因為 FileNotFoundException 繼承 IOException; 42 42 43 43 FileInputStream fis = new FileInputStream("E:\\jaa 作業室\\temp.java"); 44 44 // FileInputStream 是一個繼承了異常基本類的 IO 流; 所以同樣在使用它的時候程式員就需要對它進行處理 45 45 46 46 System.out.println("m3...end!"); 47 47 } 48 48 } 49 49 /** 看看如果讀取的路徑出現錯誤會發生什麼結果吧 50 50 * 我是入口 51 51 * m1...start! 52 52 * m2...start! 53 53 * // 這裡也出現一了一件有意思的事情: 54 54 * // 每個方法體中只有在 異常 前的值被輸出,緊跟異常之後的值直接不被讀取了 55 55 * // 從中得出一個結論: 一個方法中通過 throws 拋出的異常,在被調用時,調用者與被調用本身,異常之後的代碼將被執行中斷,不會輸出結果,異常前的值會進行輸出 56 56 * 57 57 * 58 58 * Exception in thread "main" java.io.FileNotFoundException: E:\jaa 作業室\temp.java (系統找不到指定的路徑。) 59 59 * // 這裡就是異常的處理結果了: Exception in thread "main" java.io.FileNotFoundException: E:\jaa 作業室\temp.java (系統找不到指定的路徑。) 60 60 * // 是不是發現奇妙的事情:各個方法體不斷的對異常拋出,最後在 main 方法中進行了異常處理,那為什麼方法體中就不能處理,只能在 main 中處理呢? 61 61 * // 其實 main 方法是主調方法,是一切程式的入口,方法體只有被 main 調用才能輸出結果,(當然後面還有一種方法可以在方法體中處理)而異常的處理其實 62 62 * // 也不是在 main 方法中處理的,可以看到 main 方法體上也出現了拋出異常的設置, 其實在程式員不能看到的 JVM 中,程式將異常最終交給了 63 63 * // FileNotFoundException 類處理該異常。 64 64 * 65 65 * 66 66 * at java.base/java.io.FileInputStream.open0(Native Method) 67 67 * at java.base/java.io.FileInputStream.open(FileInputStream.java:213) 68 68 * at java.base/java.io.FileInputStream.<init>(FileInputStream.java:155) 69 69 * at java.base/java.io.FileInputStream.<init>(FileInputStream.java:110) 70 70 * 71 71 * at Rview.Test17.m3(Test17.java:43) 72 72 * at Rview.Test17.m2(Test17.java:30) 73 73 * at Rview.Test17.m1(Test17.java:22) 74 74 * at Rview.Test17.main(Test17.java:15) 75 75 * // 這裡表示的是異常出現的行號索引 76 76 */Throws 深層次理解
2、try...catch... <捕捉,處理異常>: 捕捉有可能發生異常的代碼,並將它處理,不會對異常拋出,只會終止異常的向後發展
通過一個程式來瞭解 try...catch... 如何處理異常:
1 1 import java.io.FileInputStream; 2 2 import java.io.FileNotFoundException; 3 3 import java.io.FileOutputStream; 4 4 import java.io.IOException; 5 5 6 6 /** 7 7 * try...catch... 的使用方法和重點 8 8 */ 9 9 10 10 // 這是一個與 Test17 相同的程式,但我們需要對這個程式使用使用不同的異常處理機制 11 11 12 12 public class Test18 { 13 13 14 14 public static void main(String[] args) { 15 15 16 16 m1(); 17 17 } 18 18 19 19 public static void m1(){ 20 20 21 21 System.out.println("m1...start!"); 22 22 m2(); 23 23 System.out.println("m1...end!"); 24 24 } 25 25 26 26 public static void m2(){ 27 27 28 28 System.out.println("m2...start!"); 29 29 m3(); 30 30 System.out.println("m2...end!"); 31 31 } 32 32 33 33 34 34 35 35 public static void m3(){ 36 36 37 37 38 38 // try...catch... 的重點: 可以在發生異常點進行直接處理異常,當其他調用者調用時將不會發現該方法曾經有過異常的情況,同時也不會將異常感染 39 39 // 給調用者,總之可以直接對異常直接處理 40 40 41 41 // try...catch...不僅僅只能對單個異常進行捕捉處理,同時可以對多個不同類型的異常進行處理 42 42 try { 43 43 FileInputStream fis = new FileInputStream("E:\\java\\作業室\\temp.java"); 44 44 // try 中將可能發生異常的代碼段進行捕捉 45 45 fis.close(); // 再次出現了一個異常,再處理 46 46 47 47 // 以上代碼出現異常,直接進入 catch 分支,不對其後的代碼進行編譯; 48 48 System.out.println("hello "); 49 49 } 50 50 catch (FileNotFoundException e/*必須要加變數名*/) { /*將捕捉到的異常進行處理*/ 51 51 52 52 // e.printStackTrace(); // 顯示處理結果的方法 53 53 54 54 System.out.println("出現了異常,已經處理!!"); 55 55 56 56 }catch (IOException e){ 57 57 // 已經進行了異常處理,為什麼出現了錯誤? 58 58 // 兩個 catch 對不同的異常進行了處理,但是,前一個 catch 的異常處理類是最高類 Exception,而後一個 catch 的異常處理類 59 59 // FileNotFoundException 是 Exception 的一個子類,所以在進行多次的 catch 異常處理時註意子類與父類的位置關係 60 60 //(子類在前、父類在後 或者 同時是子類) 61 61 e.printStackTrace(); 62 62 } 63 63 // 同樣要進行異常處理,這次我們將使用 try... catch... 的方式處理 64 64 65 65 System.out.println("m3...end!"); 66 66 } 67 67 } 68 68 69 69 /** output result: 70 70 * m1...start! 71 71 * m2...start! 72 72 * m3...end! 73 73 * // 奇妙的事情又發生了,使用 try... catch...的方式處理異常並不影響異常之後的代碼運行 74 74 * 75 75 * m2...end! 76 76 * m1...end! 77 77 * 78 78 * 79 79 * java.io.FileNotFoundException: E:\java\作業室\temp.java (系統找不到指定的路徑。) 80 80 * // 這個結果的輸出靠 printStackTrace() 方法; 81 81 * 82 82 * at java.base/java.io.FileInputStream.open0(Native Method) 83 83 * at java.base/java.io.FileInputStream.open(FileInputStream.java:213) 84 84 * at java.base/java.io.FileInputStream.<init>(FileInputStream.java:155) 85 85 * at java.base/java.io.FileInputStream.<init>(FileInputStream.java:110) 86 86 * 87 87 * 88 88 * at Rview.Test18.m3(Test18.java:42) 89 89 * at Rview.Test18.m2(Test18.java:30) 90 90 * at Rview.Test18.m1(Test18.java:23) 91 91 * at Rview.Test18.main(Test18.java:17) 92 92 * // 同樣是異常出現所在的行號索引 93 93 */try...catch 處理方式
三、getMessage() 和 printStackTrace()
這是兩個都是命令行列印異常信息在程式中出錯的位置及原因,其兩者的區別在於:前者的簡單的列印出錯原因,後者是詳細的列印出錯的位置及原因,一起用一段程式瞭解一下吧
1 1 import java.io.FileInputStream; 2 2 import java.io.FileNotFoundException; 3 3 4 4 public class Test19 { 5 5 6 6 public static void main(String[] args) { 7 7 8 8 m2(); 9 9 } 10 10 11 11 public static void m2 (){ 12 12 m3(); 13 13 } 14 14 15 15 public static void m3(){ 16 16 try{ 17 17 FileInputStream fis = new FileInputStream("E:\\自考本科學習筆記\\英語\\基礎英語語法、English.一般現在時態.docx"); 18 18 19 19 }catch(FileNotFoundException e){ 20 20 //e.printStackTrace(); 21 21 /* printStackTrace(); 方法的使用 將詳細的描述錯誤的原因和錯誤出現的位置 22 22 java.io.FileNotFoundException: E:\自考本科學習筆記\英語\基礎英語語法、English.一般現在時態.docx (系統找不到指定的文件。) 23 23 at java.base/java.io.FileInputStream.open0(Native Method) 24 24 at java.base/java.io.FileInputStream.open(FileInputStream.java:213) 25 25 at java.base/java.io.FileInputStream.<init>(FileInputStream.java:155) 26 26 at java.base/java.io.FileInputStream.<init>(FileInputStream.java:110) 27 27 at Rview.Test19.m3(Test19.java:19) 28 28 at Rview.Test19.m2(Test19.java:14) 29 29 at Rview.Test19.main(Test19.java:10)*/ 30 30 31 31 String st =e.getMessage(); 32 32 System.out.println(st); 33 33 /* getMessage() 方法的使用,會簡單的描述錯誤的原因 34 34 E:\自考本科學習筆記\英語\基礎英語語法、English.一般現在時態.docx (系統找不到指定的文件。) 35 35 */ 36 36 37 37 // System.out.println(e.getMessage()); 另一種寫法 38 38 } 39 39 } 40 40 }getMessage 與 printStackTrace 使用
四、finally的使用
finally 關鍵字的使用: finally 是在catch之後添加使用,它的作用是無論 try...catch...是否執行,它一定會執行
1 1 import java.io.FileInputStream; 2 2 import java.io.FileNotFoundException; 3 3 import java.io.IOException; 4 4 5 5 /* finally 通常在什麼情況下使用 6 6 通常在流關閉的情況下使用,這樣可以保證關閉可以一定執行 7 7 */ 8 8 9 9 public class Test20 { 10 10 11 11 public static void main(String[] args) { 12 12 FileInputStream fis = null; 13 13 try{ 14 14 fis = new FileInputStream("E:\\自考本科學習筆記\\英語\\基礎英語語法\\English.句子成分.docx"); 15 15 16 16 System.out.println("HelloWord!"); 17 17 String s = null; 18 18 s.toString(); 19 19 20 20 }catch(FileNotFoundException e) { 21 21 e.printStackTrace(); 22 22 } finally { 23 23 try{ 24 24 if (null != fis){ 25 25 fis.close(); 26 26 } 27 27 }catch(IOException e){ 28 28 e.printStackTrace(); 29 29 } 30 30 } 31 31 } 32 32 }finally的使用
finally的面試題:
1 1 import java.io.IOException; 2 2 3 3 public class Test21 { 4 4 5 5 public static void main(String[] args) { 6 6 int result = m(); 7 7 System.out.println(result); 8 8 // output result: 100 9 9 /** 為什麼出現這種情況? finally 難道沒有執行嗎? 10 10 * 11 11 * java程式有兩個亘古不變的原則: 1、 方法體中代碼必須遵循自上而下順序依次執行 12 12 * 2、 return 語句一旦執行,整個方法必須結束 13 13 * 14 14 * */ 15 15 } 16 16 17 17 public static int m(){ 18 18 int i = 100; 19 19 try{ 20 20 return i; 21 21 } finally{ 22 22 i++; 23 23 } 24 24 25 25 /** 26 26 * 所以 該方法的底層程式執行的是這樣的程式 27 27 * public static int m(){ 28 28 * int i = 100; 29 29 * int j = i; 30 30 * i ++; 31 31 * return j; 32 32 * } 33 33 * */ 34 34 } 35 35 }finally 的面試題
細談 final、 finally、 finalize的區別
fiinal: 修飾符(關鍵字) 如果一個類被聲明為final,意味著它不能再派生新的子類,不能作為父類被繼承。因此一個類不能既被聲明為abstract,又被聲明為final的。將變數或方法聲明為final,可以保證他們使用中不被改變。被聲明為final的變數必須在聲明時給定初值,而以後的引用中只能讀取,不可修改,被聲明為final的方法也同樣只能使用,不能重載。與 static 連用將變數變為常量;
finally:在異常處理時提供 finally 塊來執行清楚操作。如果拋出一個異常,那麼相匹配的 catch 語句就會執行,然後控制就會進入finally塊;
finalize:是方法名。java 技術允許使用 finalize() 方法在垃圾收集器將對象從記憶體中清除之前做必要的清理工作。這個方法是在垃圾收集器在確定了被清理對象沒有被引用的情況下調用的。finalize 是在Object 類中定義的,因此,所有的類都繼承了它。子類可以覆蓋 finalize() 方法,來整理系統資源或者執行其他清理工作。
四、運行時異常什麼時候出現
程式在編譯階段是正常執行的,且沒有任何的錯誤,但是當運行時,就立馬出現了錯誤,並退出 JVM 這種情況在什麼時候發生呢?
110 / 0 ---------> 這個就是一個很經典的運行時異常