程序運行時,發生的不被指望的事件,它阻止了程序按照程序員的預期正常執行,這就是異常。異常發生時,是任程序自生自滅,馬上退出終止,仍是輸出錯誤給用戶?或者用C語言風格:用函數返回值做爲執行狀態?。html
Java提供了更加優秀的解決辦法:異常處理機制。前端
異常處理機制能讓程序在異常發生時,按照代碼的預先設定的異常處理邏輯,針對性地處理異常,讓程序盡最大可能恢復正常並繼續執行,且保持代碼的清晰。
Java中的異常能夠是函數中的語句執行時引起的,也能夠是程序員經過throw 語句手動拋出的,只要在Java程序中產生了異常,就會用一個對應類型的異常對象來封裝異常,JRE就會試圖尋找異常處理程序來處理異常。java
Throwable類是Java異常類型的頂層父類,一個對象只有是 Throwable 類的(直接或者間接)實例,他纔是一個異常對象,才能被異常處理機制識別。JDK中內建了一些經常使用的異常類,咱們也能夠自定義異常。程序員
Java標準庫內建了一些通用的異常,這些類以Throwable爲頂層父類。web
Throwable又派生出Error類和Exception類。spring
錯誤:Error類以及他的子類的實例,表明了JVM自己的錯誤。錯誤不能被程序員經過代碼處理,Error不多出現。所以,程序員應該關注Exception爲父類的分支下的各類異常類。數組
異常:Exception以及他的子類,表明程序運行時發送的各類不指望發生的事件。能夠被Java異常處理機制使用,是異常處理的核心。安全
整體上咱們根據Javac對異常的處理要求,將異常類分爲2類。函數
非檢查異常(unckecked exception):Error 和 RuntimeException 以及他們的子類。javac在編譯時,不會提示和發現這樣的異常,不要求在程序處理這些異常。因此若是願意,咱們能夠編寫代碼處理(使用try…catch…finally)這樣的異常,也能夠不處理。對於這些異常,咱們應該修正代碼,而不是去經過異常處理器處理 。這樣的異常發生的緣由多半是代碼寫的有問題。如除0錯誤ArithmeticException,錯誤的強制類型轉換錯誤ClassCastException,數組索引越界ArrayIndexOutOfBoundsException,使用了空對象NullPointerException等等。this
檢查異常(checked exception):除了Error 和 RuntimeException的其它異常。javac強制要求程序員爲這樣的異常作預備處理工做(使用try…catch…finally或者throws)。在方法中要麼用try-catch語句捕獲它並處理,要麼用throws子句聲明拋出它,不然編譯不會經過。這樣的異常通常是由程序的運行環境致使的。由於程序可能被運行在各類未知的環境下,而程序員沒法干預用戶如何使用他編寫的程序,因而程序員就應該爲這樣的異常時刻準備着。如SQLException , IOException,ClassNotFoundException 等。
須要明確的是:檢查和非檢查是對於javac來講的,這樣就很好理解和區分了。
1.咱們在工做的時候,項目是分模塊或者分功能開發的 ,基本不會你一我的開發一整個項目,使用自定義異常類就統一了對外異常展現的方式。
2.有時候咱們遇到某些校驗或者問題時,須要直接結束掉當前的請求,這時即可以經過拋出自定義異常來結束,若是你項目中使用了SpringMVC比較新的版本的話有控制器加強,能夠經過@ControllerAdvice註解寫一個控制器加強類來攔截自定義的異常並響應給前端相應的信息(關於springMVC控制器加強的知識有空再和你們分享)。
3.自定義異常能夠在咱們項目中某些特殊的業務邏輯時拋出異常,好比」中性」.equals(sex),性別等於中性時咱們要拋出異常,而Java是不會有這種異常的。系統中有些錯誤是符合Java語法的,但不符合咱們項目的業務邏輯。
4.使用自定義異常繼承相關的異常來拋出處理後的異常信息能夠隱藏底層的異常,這樣更安全,異常信息也更加的直觀。自定義異常能夠拋出咱們本身想要拋出的信息,能夠經過拋出的信息區分異常發生的位置,根據異常名咱們就能夠知道哪裏有異常,根據異常提示信息進行程序修改。好比空指針異常NullPointException,咱們能夠拋出信息爲「xxx爲空」定位異常位置,而不用輸出堆棧信息。
若是要自定義異常類,則擴展Exception類便可,所以這樣的自定義異常都屬於檢查異常(checked exception)。若是要自定義非檢查異常,則擴展自RuntimeException。
按照國際慣例,自定義的異常應該老是包含以下的構造函數:
下面是IOException類的完整源代碼,能夠借鑑。
/**
* MIT License
* Copyright (c) 2018 haihua.liu
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the 「Software」), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED 「AS IS」, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package cn.liuhaihua.web.exception;/**
* @ClassName: ServiceException
* @Description: 自定義service異常
* @author Liuhaihua
* @date 2018年7月5日
*
*/
public class ServiceException extends RuntimeException {private static final long serialVersionUID = 1L;
/**
* 錯誤編碼
*/
private String errorCode;/**
* 消息是否爲屬性文件中的Key
*/
private boolean propertiesKey = true;/**
* 構造一個基本異常.
*
* @param message
* 信息描述
*/
public ServiceException(String message)
{
super(message);
}/**
* 構造一個基本異常.
*
* @param errorCode
* 錯誤編碼
* @param message
* 信息描述
*/
public ServiceException(String errorCode, String message)
{
this(errorCode, message, true);
}/**
* 構造一個基本異常.
*
* @param errorCode
* 錯誤編碼
* @param message
* 信息描述
*/
public ServiceException(String errorCode, String message, Throwable cause)
{
this(errorCode, message, cause, true);
}/**
* 構造一個基本異常.
*
* @param errorCode
* 錯誤編碼
* @param message
* 信息描述
* @param propertiesKey
* 消息是否爲屬性文件中的Key
*/
public ServiceException(String errorCode, String message, boolean propertiesKey)
{
super(message);
this.setErrorCode(errorCode);
this.setPropertiesKey(propertiesKey);
}/**
* 構造一個基本異常.
*
* @param errorCode
* 錯誤編碼
* @param message
* 信息描述
*/
public ServiceException(String errorCode, String message, Throwable cause, boolean propertiesKey)
{
super(message, cause);
this.setErrorCode(errorCode);
this.setPropertiesKey(propertiesKey);
}/**
* 構造一個基本異常.
*
* @param message
* 信息描述
* @param cause
* 根異常類(能夠存入任何異常)
*/
public ServiceException(String message, Throwable cause)
{
super(message, cause);
}public String getErrorCode()
{
return errorCode;
}public void setErrorCode(String errorCode)
{
this.errorCode = errorCode;
}public boolean isPropertiesKey()
{
return propertiesKey;
}public void setPropertiesKey(boolean propertiesKey)
{
this.propertiesKey = propertiesKey;
}}
當使用多個catch語句塊來捕獲異常時,須要將父類的catch語句塊放到子類型的catch塊以後,這樣才能保證後續的catch可能被執行,不然子類型的catch將永遠沒法到達,Java編譯器會報編譯錯誤。
若是try語句塊中存在return語句,那麼首先會執行finally語句塊中的代碼,而後才返回。
若是try語句塊中存在System.exit(0)語句,那麼久不會執行finally語句塊的代碼了,由於System.exit(0)會終止當前運行的JVM。程序在JVM終止前結束執行。