異常的概念 java
任何的異常都是Throwable類(爲什麼不是接口??),而且在它之下包含兩個子類Error / Exception,而Error僅在當在Java虛擬機中發生動態鏈接失敗或其它的定位失敗的時候,Java虛擬機拋出一個Error對象。典型的簡易程序不捕獲或拋出Errors對象,你可能永遠不會遇到須要實例化Error的應用,那就讓咱們關心一下Exception。react
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 。 安全
即RuntimeException(運行時異常)
不須要try...catch...或throws 機制去處理的異常
網絡
這是JAVA認證考試中最多見的題目,事實上,runtime exception中最多見的,常常碰到的,也就5,6種,以下:
app
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 |
下面是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.如何可能的話,應該在系統級被捕捉
1、常見異常的類型與緣由。
對於Java應用程序的常見異常,筆者認爲程序開發人員要從兩個方面去了解。一是要知道有哪些常見的Java應用程序異常,二是須要知道哪些緣由可能會形成這個異常。這不只須要程序管理人員在平常工做中要注意積累,在必要的狀況下還須要去從其它渠道收集資料。筆者對此就進行一個分析,但願可以對各位程序開發人員有必定的幫助。
一、 SQLException:操做數據庫異常類。
如今的Java應用程序大部分都是依賴於數據庫運行的。當Java應用程序與數據庫進行溝通時若是產生了錯誤,就會觸發這個類。同時會將數據庫的錯誤信息經過這個類顯示給用戶。也就是說,這個操做數據庫異常類是數據庫與用戶之間異常信息傳遞的橋樑。如如今用戶往系統中插入數據,而在數據庫中規定某個字段必須惟一。當用戶插入數據的時候,若是這個字段的值跟現有的紀錄重複了,違反了數據庫的惟一性約束,此時數據庫就會跑出一個異常信息。這個信息通常用戶可能看不到,由於其發生在數據庫層面的。此時這個操做數據庫異常類就會捕捉到數據庫的這個異常信息,並將這個異常信息傳遞到前臺。如此的話,前臺用戶就能夠根據這個異常信息來分析發生錯誤的緣由。這就是這個操做數據庫異常類的主要用途。在Java應用程序中,全部數據庫操做發生異常時,都會觸發這一個類。全部此時Java應用程序自己的提示信息每每過於籠統,只是說與數據庫交互出現錯誤,沒有多大的參考價值。此時反而是數據庫的提示信息更加有使用價值。
二、 ClassCastException:數據類型轉換異常。
在Java應用程序中,有時候須要對數據類型進行轉換。這個轉換包括顯示的轉換與隱式的轉換。不過不管怎麼轉換,都必需要符合一個前提的條件,即數據類型的兼容性。若是在數據轉換的過程當中,違反了這個原則,那麼就會觸發數據類型轉換異常。如如今在應用程序中,開發人員須要將一個字符型的日期數據轉換爲數據庫所可以接受的日期型數據,此時只須要在前臺應用程序中進行控制,通常不會有問題。可是,若是前臺應用程序缺少相關的控制,如用戶在輸入日期的時候只輸入月、日信息,而沒有年份的信息。此時應用程序在進行數據類型轉換的時候,就會出現異常。根據筆者的經驗,數據類型轉換異常在應用程序開發中是一個出現的比較多的異常,也是一個比較低級的異常。由於大部分狀況下,均可以在應用程序窗口中對數據類型進行一些強制的控制。即在數據類型進行轉換以前,就保證數據類型的兼容性。如此的話,就不容易形成數據類型的轉換異常。如在只容許數值類型的字段中,能夠設置不容許用戶輸入數值之外的字符。雖說有了異常處理機制,能夠保證應用程序不會被錯誤的運行。可是在實際開發中,仍是要儘量多的預見錯誤發生的緣由,儘可能避免異常的發生。
三、 NumberFormatException:字符串轉換爲數字類型時拋出的異常。
在數據類型轉換過程當中,若是是字符型轉換爲數字型過程當中出現的問題,對於這個異常在Java程序中採用了一個獨立的異常,即NumberFormatException.如如今講字符型的數據「123456」轉換爲數值型數據時,是容許的。可是若是字符型數據中包含了非數字型的字符,如123#56,此時轉換爲數值型時就會出現異常。系統就會捕捉到這個異常,並進行處理。
Java應用程序中常見的異常類還有不少。如未找到相應類異常、不容許訪問某些類異常、文件已經結束異常、文件未找到異常、字段未找到異常等等。通常系統開發人員均可以根據這個異常名來判斷當前異常的類型。雖然不錯,可是好記性不如爛筆頭。程序開發人員在必要的時候(特別是存在自定義異常的時候),最後手頭有一份異常明細表。如此的話,不管是應用程序在調試過程當中發現問題,仍是運行過程當中接到用戶的投訴,均可以及時的根據異常名字來找到異常發生的緣由。從而能夠在最短期內解決異常,恢復應用程序的正常運行。
2、異常管理的實用建議。
對於操做數據庫異常來講,Java應用程序只提供了一個異常類。故光憑Java應用程序的錯誤信息,每每不可以幫助應用程序人員排除錯誤的緣由。只可以指名是應用程序錯誤仍是數據庫錯誤致使的這個異常。爲了更進一步指明問題的緣由,在數據庫層面定義異常的時候,最好可以說明具體的緣由。如前臺應用程序可能會調用數據庫的函數或者過程。此時在數據庫的函數或者過程當中作好可以說明某個異常的具體緣由。如根據某個基礎表生成另外一張表的時候,某個字段不可以爲空等等。將這些異常信息說明清楚後,若是真的遇到相似的異常時,操做數據庫異常類就會將數據庫的異常信息反會給前臺用戶。從而有利於用戶尋找問題的緣由,並在最短期內改正。固然,這須要Java程序員與數據庫設計人員進行協調。
其次須要注意的是,異常並非常態。也就是說,大部分異常能夠經過前提的合理預見與預防,來消除。如設計到四則運算,能夠在前臺應用程序窗口中限制在除數字段內輸入0值等手段來消除應用程序運行中可能產生的異常。不過這每每要求應用程序開發人員有比較豐富的工做經驗以及由比較嚴密的思惟邏輯。雖然這有必定的難度,可是筆者認爲程序開發人員仍是應該往這方面努力,而不要總是讓用戶做爲你的試驗品,讓用戶來發現應用程序中的設計Bug.筆者認爲,只有一些實在是程序人員沒法控制的因素才容許拋出異常。若是應用程序開發人員可以意識到這種錯誤、可是仍然沒有引發重視或者採起有效的措施防止出現這種異常,那麼筆者是不容許的。
ArithmeticException(除數爲0的異常),
BufferOverflowException(緩衝區上溢異常),
BufferUnderflowException(緩衝區下溢異常),
IndexOutOfBoundsException(出界異常),
NullPointerException(空指針異常),
EmptyStackException(空棧異常),
IllegalArgumentException(不合法的參數異常),
NegativeArraySizeException(建立大小爲負的數組,則拋出該異常),
NoSuchElementException(由 Enumeration
的 nextElement
方法拋出,代表枚舉中沒有更多的元素),
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 的爭論。你能夠根據本身的實際狀況定製本身異常處理,我堅信咱們將有更好的辦法來處理咱們代碼中的異常。