Checked 和 UnChecked 異常 的使用場合


異常的概念   

       任何的異常都是Throwable類(為何不是接口??),並且在它之下包含兩個子類Error / Exception,而Error僅在當在Java虛擬機中發生動態連接失敗或其它的定位失敗的時候,Java虛擬機拋出一個Error對象。典型的簡易程序不捕獲或拋出Errors對象,你可能永遠不會遇到需要實例化Error的應用,那就讓我們關心一下Exception。

       Exception中比較重要的就是RuntimeException(運行時異常)-可能在執行方法期間拋出但未被捕獲的 RuntimeException 的任何子類都無需在 throws 子句中進行聲明,也就是說你的應用應該不去“關心”(說不關心是不負責任的,但只是你不應該試圖實例化它的字類)。  RuntimeException,就如同你不應該關心Error的產生與處理一樣!RuntimeException描述的是程序的錯誤引起來的,因該由程序負擔這個責任!

( 從責任這個角度看Error屬於JVM需要負擔的責任;RuntimeException是程序應該負擔的責任;checked exception 是具體應用負擔的責任 

        除了Error與RuntimeException,其他剩下的異常都是你需要關心的,而這些異常類統稱為Checked Exception,至於Error與RuntimeException則被統稱為Unchecked Exception.

關於 Java 中引入的 Checked Exceptions,目前存在着很多反對意見。正方的觀點是引入 Checked Exceptions,可以增加程度的魯棒性。反方的觀點是 Checked Exceptions 很少被開發人員正確使用過,並且降低了程序開發的生產率和代碼的執行效率。  

Java 中定義了兩類異常 :  

1) Checked exception: 這類異常都是Exception的子類 。異常的向上拋出機制進行處理,如果子類可能產生A異常,那么在父類中也必須throws A異常。可能導致的問題:代碼效率低,耦合度過高。C#中就沒有使用這種異常機制。 

2)  Unchecked exception: 這類異常都是RuntimeException的子類 ,雖然RuntimeException同樣也是Exception的子類,但是它們是特殊的,它們不能通過client code來試圖解決,所以稱為Unchecked exception 。  

   

  • 什么是unchecked異常?

即RuntimeException(運行時異常) 
不需要try...catch...或throws 機制去處理的異常 

  • 列舉最常用的五種RuntimeException:       

這是JAVA認證考試中最常見的題目,事實上,runtime exception中最常見的,經常碰到的,也就5,6種,如下: 

 ArithmeticException int a=0; 
int b= 3/a;
 ClassCastException: Object x = new Integer(0); 
System.out.println((String)x);
  IndexOutOfBoundsExceptio n 
       ArrayIndexOutOfBoundsExc eption,             
       StringIndexOutOfBoundsEx ception              
int [] numbers = { 1, 2, 3 }; 
int sum = numbers[3];
IllegalArgumentException 
       NumberFormatException 
int a = Interger.parseInt("test");             
NullPointerExceptionexte nds   
  • 除了RuntimeException,其他繼承自java.lang.Exception得異常統稱為Checked Exception,他們有多少種呢?

下面是JDK API中列出的異常類: 
除了 RuntimeException以外的,都是checked Exception 

AclNotFoundException, ActivationException, AlreadyBoundException, ApplicationException, AWTException, BackingStoreException, BadAttributeValueExpExce ption, BadBinaryOpValueExpExcep tion, BadLocationException, BadStringOperationExcept ion, BrokenBarrierException, CertificateException, ClassNotFoundException, CloneNotSupportedExcepti on, DataFormatException, DatatypeConfigurationExc eption, DestroyFailedException, ExecutionException, ExpandVetoException, FontFormatException, GeneralSecurityException , GSSException, IllegalAccessException, IllegalClassFormatExcept ion, InstantiationException, InterruptedException, IntrospectionException, InvalidApplicationExcept ion, InvalidMidiDataException , InvalidPreferencesFormat Exception, InvalidTargetObjectTypeE xception, InvocationTargetExceptio n, IOException, JAXBException, JMException, KeySelectorException, LastOwnerException, LineUnavailableException , MarshalException, MidiUnavailableException , MimeTypeParseException, MimeTypeParseException, NamingException, NoninvertibleTransformEx ception, NoSuchFieldException, NoSuchMethodException, NotBoundException, NotOwnerException, ParseException, ParserConfigurationExcep tion, PrinterException, PrintException, PrivilegedActionExceptio n, PropertyVetoException, RefreshFailedException, RemarshalException,  RuntimeException , SAXException, ScriptException, ServerNotActiveException , SOAPException, SQLException, TimeoutException, TooManyListenersExceptio n, TransformerException, TransformException, UnmodifiableClassExcepti on, UnsupportedAudioFileExce ption, UnsupportedCallbackExcep tion, UnsupportedFlavorExcepti on, UnsupportedLookAndFeelEx ception, URIReferenceException, URISyntaxException, UserException, XAException, XMLParseException, XMLSignatureException, XMLStreamException, XPathException 

 

checked exception是需要強制catch的異常,你在調用這個方法的時候,你如果不catch這個異常,那么編譯器就會報錯,比如說我們讀寫文件的時候會catch IOException,執行數據庫操作會有SQLException等  
UnChecked Exception是RuntimeException,也就是說運行時的異常,這種異常不是必須需要catch的,你是無法預料的,比如說你在調用一個list.szie()的時候,如果這個list為null,那么就會報NUllPointerException,而這個異常就是RuntimeException,也就是UnChecked Exception 
 

Error和RuntimeException及其子類是unchecked exception.其他exception是checked exception.  
checked exception可以出現在throws子句中,unchecked exception不可以。  
Error是java自己的錯誤或者諸如內存耗盡等嚴重錯誤,是不可抗拒的,顯然沒有捕捉的必要,而且也沒有辦法捕捉。  
RuntimeException是你的程序有邏輯錯誤,是程序員應該積極避免其出現的異常。比如NullPointerException等,完全是程序員馬虎出的錯。當遇到這種錯誤時,java將這個錯誤自動捕捉到,比如顯示到concole里,然后繼續運行。而checked exception如果不捕捉則會導致程序終止。 
  

error 表示恢復不是不可能但很困難的情況下的一種嚴重問題。比如說內存溢出。不可能指望程序能處理這樣的情況。

exception 表示一種設計或實現問題。也就是說,它表示如果程序運行正常,從不會發生的情況

error和excption的區別 
  
Error的繼承關系: 
  
java.lang.Object

--java.lang.Throwable 
  
    --java.lang.Error 
  
Exception的繼承關系: 
  
java.lang.Object 
  
--java.lang.Throwable 
  
    --java.lang.Exception 
  
二者的不同之處: 
  
Exception: 
  
1.可以是可被控制(checked) 或不可控制的(unchecked) 
  
2.表示一個由程序員導致的錯誤 
  
3.應該在應用程序級被處理 
  
Error: 
  
1.總是不可控制的(unchecked) 
  
2.經常用來用於表示系統錯誤或低層資源的錯誤 
  
3.如何可能的話,應該在系統級被捕捉

 

 一、常見異常的類型與原因。  

    對於Java應用程序的常見異常,筆者認為程序開發人員要從兩個方面去了解。一是要知道有哪些常見的Java應用程序異常,二是需要知道哪些原因可能會造成這個異常。這不僅需要程序管理人員在日常工作中要注意積累,在必要的情況下還需要去從其它渠道收集資料。筆者對此就進行一個分析,希望能夠對各位程序開發人員有一定的幫助。  

    1、 SQLException:操作數據庫異常類。  

    現在的Java應用程序大部分都是依賴於數據庫運行的。當Java應用程序與數據庫進行溝通時如果產生了錯誤,就會觸發這個類。同時會將數據庫的錯誤信息通過這個類顯示給用戶。也就是說,這個操作數據庫異常類是數據庫與用戶之間異常信息傳遞的橋梁。如現在用戶往系統中插入數據,而在數據庫中規定某個字段必須唯一。當用戶插入數據的時候,如果這個字段的值跟現有的紀錄重復了,違反了數據庫的唯一性約束,此時數據庫就會跑出一個異常信息。這個信息一般用戶可能看不到,因為其發生在數據庫層面的。此時這個操作數據庫異常類就會捕捉到數據庫的這個異常信息,並將這個異常信息傳遞到前台。如此的話,前台用戶就可以根據這個異常信息來分析發生錯誤的原因。這就是這個操作數據庫異常類的主要用途。在Java應用程序中,所有數據庫操作發生異常時,都會觸發這一個類。所有此時Java應用程序本身的提示信息往往過於籠統,只是說與數據庫交互出現錯誤,沒有多大的參考價值。此時反而是數據庫的提示信息更加有使用價值。  

    2、 ClassCastException:數據類型轉換異常。  

    在Java應用程序中,有時候需要對數據類型進行轉換。這個轉換包括顯示的轉換與隱式的轉換。不過無論怎么轉換,都必須要符合一個前提的條件,即數據類型的兼容性。如果在數據轉換的過程中,違反了這個原則,那么就會觸發數據類型轉換異常。如現在在應用程序中,開發人員需要將一個字符型的日期數據轉換為數據庫所能夠接受的日期型數據,此時只需要在前台應用程序中進行控制,一般不會有問題。但是,如果前台應用程序缺乏相關的控制,如用戶在輸入日期的時候只輸入月、日信息,而沒有年份的信息。此時應用程序在進行數據類型轉換的時候,就會出現異常。根據筆者的經驗,數據類型轉換異常在應用程序開發中是一個出現的比較多的異常,也是一個比較低級的異常。因為大部分情況下,都可以在應用程序窗口中對數據類型進行一些強制的控制。即在數據類型進行轉換之前,就保證數據類型的兼容性。如此的話,就不容易造成數據類型的轉換異常。如在只允許數值類型的字段中,可以設置不允許用戶輸入數值以外的字符。雖然說有了異常處理機制,可以保證應用程序不會被錯誤的運行。但是在實際開發中,還是要盡可能多的預見錯誤發生的原因,盡量避免異常的發生。  

    3、 NumberFormatException:字符串轉換為數字類型時拋出的異常。  

    在數據類型轉換過程中,如果是字符型轉換為數字型過程中出現的問題,對於這個異常在Java程序中采用了一個獨立的異常,即NumberFormatException.如現在講字符型的數據“123456”轉換為數值型數據時,是允許的。但是如果字符型數據中包含了非數字型的字符,如123#56,此時轉換為數值型時就會出現異常。系統就會捕捉到這個異常,並進行處理。  

Java應用程序中常見的異常類還有很多。如未找到相應類異常、不允許訪問某些類異常、文件已經結束異常、文件未找到異常、字段未找到異常等等。一般系統開發人員都可以根據這個異常名來判斷當前異常的類型。雖然不錯,但是好記性不如爛筆頭。程序開發人員在必要的時候(特別是存在自定義異常的時候),最后手頭有一份異常明細表。如此的話,無論是應用程序在調試過程中發現問題,還是運行過程中接到用戶的投訴,都可以及時的根據異常名字來找到異常發生的原因。從而可以在最短時間內解決異常,恢復應用程序的正常運行。 

    二、異常管理的實用建議。  

    對於操作數據庫異常來說,Java應用程序只提供了一個異常類。故光憑Java應用程序的錯誤信息,往往不能夠幫助應用程序人員排除錯誤的原因。只能夠指名是應用程序錯誤還是數據庫錯誤導致的這個異常。為了更進一步指明問題的原因,在數據庫層面定義異常的時候,最好能夠說明具體的原因。如前台應用程序可能會調用數據庫的函數或者過程。此時在數據庫的函數或者過程中做好能夠說明某個異常的具體原因。如根據某個基礎表生成另一張表的時候,某個字段不能夠為空等等。將這些異常信息說明清楚后,如果真的遇到類似的異常時,操作數據庫異常類就會將數據庫的異常信息反會給前台用戶。從而有利於用戶尋找問題的原因,並在最短時間內改正。當然,這需要Java程序員與數據庫設計人員進行協調。  

    其次需要注意的是,異常並不是常態。也就是說,大部分異常可以通過前提的合理預見與預防,來消除。如設計到四則運算,可以在前台應用程序窗口中限制在除數字段內輸入0值等手段來消除應用程序運行中可能產生的異常。不過這往往要求應用程序開發人員有比較豐富的工作經驗以及由比較嚴密的思維邏輯。雖然這有一定的難度,但是筆者認為程序開發人員還是應該往這方面努力,而不要老是讓用戶作為你的試驗品,讓用戶來發現應用程序中的設計Bug.筆者認為,只有一些實在是程序人員無法控制的因素才允許拋出異常。如果應用程序開發人員能夠意識到這種錯誤、但是仍然沒有引起重視或者采取有效的措施防止出現這種異常,那么筆者是不允許的。

ArithmeticException(除數為0的異常),

BufferOverflowException(緩沖區上溢異常),

BufferUnderflowException(緩沖區下溢異常),

IndexOutOfBoundsException(出界異常),

NullPointerException(空指針異常),

EmptyStackException(空棧異常),

IllegalArgumentException(不合法的參數異常),

NegativeArraySizeException(創建大小為負的數組,則拋出該異常),

NoSuchElementException(由 EnumerationnextElement 方法拋出,表明枚舉中沒有更多的元素),

SecurityException(由安全管理器拋出的異常,指示存在安全侵犯),

SystemException,

UndeclaredThrowableException(由調用處理程序拋出的經過檢查的未聲明異常)

1. java.lang.NullPointerException 
異常的解釋是"程序遇上了空指針",簡單地說就是調用了未經初始化的對象或者是不存在的對象,即把數組的初始化和數組元素的初始化混淆起來了。數組的初始化是對數組分配需要的空間,而初始化后的數組,其中的元素並沒有實例化,依然是空的,所以還需要對每個元素都進行初始化(如果要調用的話) 
2. java.lang.ClassNotFoundException 異常的解釋是"指定的類不存在"。 
3. java.lang.ArithmeticException 這個異常的解釋是"數學運算異常",比如程序中出現了除以零這樣的運算就會出這樣的異常。 
4. java.lang.ArrayIndexOutOfBoundsException 
異常的解釋是"數組下標越界",現在程序中大多都有對數組的操作,因此在調用數組的時候一定要認真檢查,看自己調用的下標是不是超出了數組的范圍,一般來說,顯示(即直接用常數當下標)調用不太容易出這樣的錯,但隱式(即用變量表示下標)調用就經常出錯了,還有一種情況,是程序中定義的數組的長度是通過某些特定方法決定的,不是事先聲明的,這個時候,最好先查看一下數組的length,以免出現這個異常。     
5. java.lang.IllegalArgumentException 
這個異常的解釋是"方法的參數錯誤",比如g.setColor(int red,int green,int blue)這個方法中的三個值,如果有超過255的也會出現這個異常,因此一旦發現這個異常,我們要做的,就是趕緊去檢查一下方法調用中的參數傳遞是不是出現了錯誤。 
6. java.lang.IllegalAccessException 
這個異常的解釋是"沒有訪問權限",當應用程序要調用一個類,但當前的方法即沒有對該類的訪問權限便會出現這個異常。對程序中用了Package的情況下要注意這個異常

一.異常介紹

任何的異常都是 Throwable 類,並且在它之下包含兩個字類 Error / Exception ,而Error 僅在當在 Java 虛擬機中發生動態連接失敗或其它的定位失敗的時候, Java 虛擬機拋出一個 Error 對象。典型的簡易程序不捕捉或拋出 Errors 對象,你可能永遠不會碰到需要實例化 Error 的應用,那就讓我們關心一下 Exception 

Unchecked Exception . :包括  Error  RuntimeException.  這類異常都是RuntimeException 的子類。

Checked Exception : 除了 Error  RuntimeException ,其他剩下的異常  這類異常都是 Exception 的子類   。在編譯時在語法上必須處理的異常,因此必須在語法上以try..catch 加以處理;

二.設計異常的最佳實踐    Best Practises for Designing the API 

 1    當要決定是采用 checked exception 還是 Unchecked exception 的時候,你要問自己一個問題,  如果這種異常一旦拋出,客戶端會做怎樣的補救? 

[ 原文: When deciding on checked exceptions vs. unchecked exceptions, ask yourself, "What action can the client code take when the exception occurs?"]

如果客戶端可以通過其他的方法恢復異常,那么這種異常就是 checked exception ;如果客戶端對出現的這種異常無能為力,那么這種異常就是 Unchecked exception ;從使用上講,當異常出現的時候要做一些試圖恢復它的動作而不要僅僅的打印它的信息,總的來說,看下表:

Client's reaction when exception happens

Exception type

Client code cannot do anything

Make it an unchecked exception

Client code will take some useful recovery action based on information in exception

Make it a checked exception

此外,盡量使用 unchecked exception 來處理編程錯誤:因為 unchecked exception 不用使客戶端代碼顯示的處理它們,它們自己會在出現的地方掛起程序並打印出異常信息。 Java API 中提供了豐富的 unchecked excetpion ,譬如: NullPointerException , IllegalArgumentException    IllegalStateException 等,因此我一般使用這些標准的異常類而不願親自創建新的異常類,這樣使我的代碼易於理解並避免的過多的消耗內存。   

  2    保護封裝性( Preserve encapsulation 

不要讓你要拋出的 checked exception 升級到較高的層次。例如,不要讓SQLException 延伸到業務層。業務層並不需要(不關心?) SQLException 。你有兩種方法來解決這種問題:

l          轉變 SQLException 為另外一個 checked exception ,如果客戶端並不需要恢復這種異常的話;

l          轉變 SQLException 為一個 unchecked exception ,如果客戶端對這種異常無能為力的話;

多數情況下,客戶端代碼都是對 SQLException 無能為力的,因此你要毫不猶豫的把它轉變為一個 unchecked exception ,看看下邊的代碼:

  public void dataAccessCode(){

    try{

        ..some code that throws SQLException           

    }catch(SQLException ex){

        ex.printStacktrace();

    }

}

上邊的 catch 塊僅僅打印異常信息而沒有任何的直接操作,這是情有可原的,因為對於     SQLException 你還奢望客戶端做些什么呢?(但是顯然這種就象什么事情都沒發生一樣的做法是不可取的)那么有沒有另外一種更加可行的方法呢?

  public void dataAccessCode(){

    try{

        ..some code that throws SQLException           

    }catch(SQLException ex){

        throw new RuntimeException(ex);

    }

}

  上邊的做法是把 SQLException 轉換為 RuntimeException ,一旦 SQLException 被拋出,那么程序將拋出 RuntimeException, 此時程序被掛起並返回客戶端異常信息。

  如果你有足夠的信心恢復它當 SQLException 被拋出的時候,那么你也可以把它轉換為一個有意義的 checked exception,  但是我發現在大多時候拋出 RuntimeException 已經足夠用了。

  3    不要創建沒有意義的異常( Try not to create new custom exceptions if they do not have useful information for client code. 

看看下面的代碼有什么問題?

public class DuplicateUsernameException           

    extends Exception {}

它除了有一個  意義明確  的名字以外沒有任何有用的信息了。不要忘記 Exception 跟其他的 Java 類一樣,客戶端可以調用其中的方法來得到更多的信息。

我們可以為其添加一些必要的方法,如下:

public class DuplicateUsernameException           

    extends Exception {

    public DuplicateUsernameException

        (String username){....}

    public String requestedUsername(){...}

    public String[] availableNames(){...}

}

在新的代碼中有兩個有用的方法: reqeuestedUsername(), 客戶但可以通過它得到請求的名稱; availableNames(), 客戶端可以通過它得到一組有用的 usernames 。這樣客戶端在得到其返回的信息來明確自己的操作失敗的原因。但是如果你不想添加更多的信息,那么你可以拋出一個標准的 Exception:

throw new Exception("Username already taken");           

更甚的情況,如果你認為客戶端並不想用過多的操作而僅僅想看到異常信息,你可以拋出一個 unchecked exception:

throw new RuntimeException("Username already taken");           

另外,你可以提供一個方法來驗證該 username 是否被占用。

  很有必要再重申一下, checked exception 應該讓客戶端從中得到豐富的信息。要想讓你的代碼更加易讀,請傾向於用 unchecked excetpion 來處理程序中的錯誤 Prefer unchecked exceptions for all programmatic errors )。

4   Document exceptions.

你可以通過 Javadoc’s @throws  標簽來說明( document )你的 API 中要拋出 checked exception 或者 unchecked exception 。然而,我更傾向於使用來單元測試來說明(document )異常。不管你采用哪中方式,你要讓客戶端代碼知道你的 API 中所要拋出的異常。這里有一個用單元測試來測試 IndexOutOfBoundsException 的例子:

public void testIndexOutOfBoundsException() {

    ArrayList blankList = new ArrayList();

    try {

        blankList.get(10);

        fail("Should raise an IndexOutOfBoundsException");           

    } catch (IndexOutOfBoundsException success) {}

}

 

上邊的代碼在請求 blankList.get(10) 的時候會拋出 IndexOutOfBoundsException, 如果沒有被拋出,將 fail( "Should raise an IndexOutOfBoundsException" ) 顯示說明該測試失敗。通過書寫測試異常的單元測試,你不但可以看到異常是怎樣的工作的,而且你可以讓你的代碼變得越來越健壯。

  三. 使用異常的最佳實踐( Best Practices for Using Exceptions 

1    總是要做一些清理工作  Always clean up after yourself 

如果你使用一些資源例如數據庫連接或者網絡連接,請記住要做一些清理工作(如關閉數據庫連接或者網絡連接),如果你的 API 拋出 Unchecked exception ,那么你要用try-finally 來做必要的清理工作:

public void dataAccessCode(){

    Connection conn = null;

    try{

        conn = getConnection();

        ..some code that throws SQLException

    }catch(SQLException ex){

        ex.printStacktrace();

    } finally{

        DBUtil.closeConnection(conn);

    }

}

class DBUtil{

    public static void closeConnection

        (Connection conn){

        try{

            conn.close();

        } catch(SQLException ex){

            logger.error("Cannot close connection");           

            throw new RuntimeException(ex);

        }

    }

}

DBUtil 是一個工具類來關閉 Connection. 有必要的說的使用的 finally 的重要性是不管程序是否碰到異常,它都會被執行。在上邊的例子中, finally 中關閉連接,如果在關閉連接的時候出現錯誤就拋出 RuntimeException.

  2    不要使用異常來控制流程( Never use exceptions for flow control 

下邊代碼中, MaximumCountReachedException 被用於控制流程:

public void useExceptionsForFlowControl() {

   try {

        while (true) {

            increaseCount();

        }

    } catch (MaximumCountReachedException ex) {

    }

    //Continue execution

}

 

public void increaseCount()

    throws MaximumCountReachedException {

    if (count >= 5000)

        throw new MaximumCountReachedException();           

}

上邊的 useExceptionsForFlowControl() 用一個無限循環來增加count 直到拋出異常,這種做法並沒有說讓代碼不易讀,但是它是程序執行效率降低。

記住,只在要會拋出異常的地方進行異常處理。

3 . 不要忽略異常

當有異常被拋出的時候,如果你不想恢復它,那么你要毫不猶豫的將其轉換為unchecked exception ,而不是用一個空的catch塊或者什么也不做來忽略它,以至於從表面來看象是什么也沒有發生一樣。

4 . 不要捕獲頂層的Exception

unchecked exception 都是RuntimeException 的子類,RuntimeException又繼承Exception,因此,如果單純的捕獲Exception,那么你同樣也捕獲了RuntimeException,如下代碼:

try{

..

}catch(Exception ex){           

}

一旦你寫出了上邊的代碼(注意 catch 塊是空的),它將忽略所有的異常,包括unchecked exception.

5   Log exceptions just once

     Logging the same exception stack trace more than once can confuse the programmer examining the stack trace about the original source of exception. So just log it once.

總結

這里給出了一些關於異常處理的一些最佳實踐,我並不想開始另一輪的關於 checked exception    unchecked exception 的爭論。你可以根據自己的實際情況定制自己異常處理,我堅信我們將有更好的辦法來處理我們代碼中的異常。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM