try塊與if語句不一樣, try塊后的花括號不可以省略,即使只有一行代碼,也不能省略花括號。與之類似的是 catch塊后的花括號也不可以省略。
try塊里聲明的變量是代碼塊內局部變量,它只在try塊內有效,在catch塊中不能訪問。
不管try塊中的代碼是否出現異常,也不管哪一個catch塊被執行,甚至在try塊或者catch塊中執行了return語句,finally塊總會被執行。
異常處理語法結構中只有try塊是必需的,也就是說,如果沒有try塊,則不能有后面的catch塊和finally塊,catch塊和finally塊是可選的,但 catch塊和finally塊至少出現其中之一,也可以同時出現多個catch塊,捕獲父類異常的catch塊必需位于捕獲子類異常后面,finally塊必需位于所有catch塊之后。
雖然return語句也強制方法結束,但一定會先執行finally塊里的代碼。如果在異常處理代碼中使用System.exit(1)語句來退出虛擬機,則finally塊將失去執行的機會。
通常情況下,不要在finally塊中使用return或者throw等導致方法終止的語句,一旦使用將會導致try塊、catch塊中的retrun、throw語句失效。
當java程序執行try塊、catch塊時遇到了return 或throw語句,這兩個語句都會導致該方法立即結束,但是系統執行這兩個語句并不會結束該方法,而是去勛章該異常處理流程中是否包含finally塊,如果沒有程序立即執行return或throw語句,方法終止; 如果有finally塊,系統立即開始執行finally塊——只有當finally塊執行完成后,系統才會再次跳回來執行try塊、catch塊里的return或throw語句;如果finally塊里也使用return或throw等導致方法終止的語句,finally塊已經終止了方法,系統將不會調回去執行try塊、catch塊里的任何代碼 。
下面是一道面試題
public class ThreeException extends Exception{ } public class TestClass { static int count = 0; public static void main(String[] args) { while(true){ try{ if(count++==0) throw new ThreeException(); System.out.println("No Exception"); }catch(ThreeException e){ System.err.println("ThreeException"); }catch(Exception e){ System.err.println("Exception"); }finally{ System.err.println("Finally"); if(count==2) break; } } } }
打印結果:
ThreeException
Finally
No Exception
Finally
Java異常被分為兩大類:checked異常和Runtime異常(運行時異常)。所有RuntimeException類及其子類的實例被稱為Runtime異常;不是RuntimeException類及其子類的異常實例則被稱為checked異常。
對于checked異常的處理方式有如下兩種:
1、當前方法明確知道如何處理該異常,程序應該使用try...catch塊來捕獲該異常,然后在對應的catch塊中修復該異常
2、當前方法不知道如何處理這種異常,應該在定義該方法時聲明拋出該異常。
使用throws聲明拋出異常的思路是,當前方法不知道如何處理這種類型的異常,該異常應該由上一級調用者處理,如果main方法也不知道如何處理這種類型的異常,也可以 使用throws聲明拋出異常,該異常交給JVM處理。
JVM對異常處理的方法是打印異常的跟蹤棧信息,并中止程序運行,這就是前面程序在遇到異常后自動結束的原因。
如果某段代碼中調用了一個帶throws聲明的方法,該方法聲明拋出了Checked異常,則表明該方法希望它的調用者來處理該異常。也就是說,調用該方法時要么放在try塊中顯示捕獲該異常,要么放在另一個帶throws聲明拋出的方法中。
使用throws聲明拋出異常時有一個限制,就是方法重寫時“兩小”中的一條規則: 子類方法聲明拋出的異常類型應該是父類方法聲明拋出的異常類型的子類或相同,子類方法聲明拋出的異常不允許比父類方法聲明拋出的異常多。
如果需要在程序中自行拋出異常,則應該使用throw語句,throw語句可以單獨使用,throw語句拋出的不是異常類,而是一個異常實例,而且每次只能拋出一個異常實例。
當java運行時接受到用戶自行拋出的異常時,同樣會中止當前的執行流,跳到該異常對應的catch塊,有該catch塊來處理該異常。也就是說,不管是系統自動拋出異常,還是程序員手動拋出的異常,java運行時環境對異常的處理沒有任何差別。
package com.hb.exception; public class ThrowTest { public static void main(String[]args){ System.out.println("ddd"); try{ //調用聲明拋出Checked異常的方法,要么顯示捕獲該異常 //要么在main方法中再次聲明拋出 throwChecked(2); }catch(Exception e){ System.out.println(e.getMessage()); } //調用聲明拋出Runtime異常的方法既可以顯示捕獲該異常,也可以不理會該異常 throwRuntime(3); } public static void throwChecked(int a) throws Exception{ if(a > 0){ //自行拋出Exception 異常 //該代碼必須處于try塊里,或出于帶throws聲明的方法中 throw new Exception("a的值大于0,不符合要求"); } } public static void throwRuntime(int a){ if(a > 0){ // 自行拋出RuntimeException異常,既可以顯示捕獲該異常 // 也可完全不理會該異常,把該異常交給該方法調用者處理 throw new RuntimeException("a的值大于0,不符合要求"); } } }
用戶自定義異常都應該繼承Exception基類,如果希望自定義Runtime異常,則應該繼承RuntimeException基類。定義異常類時通常需要提供兩個構造器:一個是無參數的構造器;另一個是帶一個字符串參數的構造器,這個字符串將作為該異常對象的描述信息(也就是異常對象的getMessage()方法返回的值)。
public class MyException extends Exception{ //無參數的構造器 public MyException(){} //帶一個字符串參數的構造器 public MyException(String msg){ super(msg); } }
如果需要自定義Runtime異常,只需要將繼承類Exception改為RuntimeException基類即可。
雖然printStackTrace()方法可以很方便地用于追蹤異常的發生情況,可以用它來調試程序,但是在最后發布的程序中,應該避免使用它;而應該對捕獲的異常進行適當的處理,而不是簡單地將異常的跟蹤棧信息打印出來。
必須指出:異常處理機制的初衷是將不可預料的異常處理代碼和正常的業務邏輯處理分離,因此絕不要使用異常處理來替代正常的業務邏輯判斷。
異常機制的效率比正常流程控制效率差,所以不要使用異常處理來替代正常的程序流程控制。
把捕獲一個異常然后接著拋出另一個異常,并把原始異常信息保存下來是一種典型的鏈式處理,也被稱為“異常鏈”。
這種把原始異常信息隱藏起來,僅向上提供必要的異常提示信息的處理方式,可以保證底層異常不會擴散到表現層,可以避免向上暴露太多的實現細節,這完全符合面向獨享的封裝原則
不要使用過于龐大的try塊:
try塊里的代碼過于龐大導致業務過于復雜,會造成try塊中出現異常的可能性大大增加,從而導致分析異常原因的難度也加大,緊隨后面的catch塊才可以針對不同的處理邏輯關系難度也加大,反而增加了編程復雜度。
正確做法是將大塊的try塊分割成多個可能出現異常的程序段落,并把他們房子單獨的try塊中,從而分別捕獲并處理異常。
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
