異常 Exception Throwable Error 基礎 [MD]


博文地址

我的GitHub 我的博客 我的微信 我的郵箱
baiqiantao baiqiantao bqt20094 baiqiantao@sina.com

基礎知識

異常簡介

異常就是java通過面向對象的思想將不正常情況封裝成了對象,用異常類對其進行描述。

異常機制

  • 如果函數正常流程返回的是result,那么異常返回的是Exception對象。正常流程用return關鍵字返回,異常流程用throw關鍵字返回。
  • 函數拋出異常后,使用try-catch機制處理異常。運行時搜索調用棧找到處理異常的函數調用。

異常機制的優點

  • 把異常處理流程和正常流程代碼分開編寫
  • 使用錯誤碼的場景對外提供API時,使用者可能不會注意到可能產生哪些錯誤;而使用Checked Exception通過編譯時機制提醒使用者注意可能發生的錯誤
  • 報告發生異常時的調用棧,方便定位問題
  • 易於根據不同類型的問題進行分組,比如IOException是所有的IO類異常的父類

try catch finally 語句

  • try語句中若檢測到異常(Throwable)會將相關的信息封裝成異常對象,然后傳遞給catch,catch捕獲到后對異常進行處理。
  • try中是一個獨立的代碼塊,在其中定義的變量只在該代碼塊中有效,如果要在try以外繼續使用,需要在try外進行聲明。
  • try中如果可能會捕獲到多個異常,那么應該(建議)有對應個數的catch進行針對性處理。
  • 一個try對應多個catch語句時,后面的catch子句中的參數類型只能是前面catch字句中參數類型的同級或父類,而不能是子類,也不能是同一個類型。
  • 一個try對應多個catch語句時,若前面的catch子句中的參數類型和實際捕獲到的異常類型一致,則只執行此catch語句,而不會再匹配或執行下面的catch字句
  • finally語句里通常用來關閉資源。比如:數據庫資源,IO資源等。

子類在覆蓋父類方法時

  • 子類的方法只能拋出父類的方法拋出的異常或者該異常的子類,而不能拋出該異常的父類或其他類型的異常
  • 如果父類的方法拋出多個異常,那么子類的該方法只能拋出父類該方法拋出的異常的子集
  • 如果父類的方法沒有拋出異常,那么子類覆蓋父類的該方法時也不能拋,如果有異常只能使用 try catch 語句

throw 語句

  • throw 語句用於拋出異常對象,一條 throw 語句只能拋出一個異常對象,throw 語句拋出的異常也可以被 try 語句捕獲
  • throw 語句之后不能再有其他語句,因為 throw 語句后面的代碼是執行不到的
  • RuntimeException 以及其子類可以在函數中通過 throw 拋出而不用在函數上聲明。

異常體系

  • Throwable
    • Error:程序外部發生錯誤導致程序發生的異常,比如 OutOfMemoryErrorrStackOverflowError
    • Exception:表示程序需要捕捉、需要處理的異常,是由於程序設計的不完善而出現的問題
    • RuntimeException:運行時異常,即 Unchecked Exceptione,是那些可能在JVM正常運行期間拋出的異常,通常是邏輯錯誤或者API調用異常,可以即不必捕獲也不拋出,可以通過 UncaughtExceptionHandler 處理
    • Checked Exceptione:編譯時異常,錯誤碼的變種形式,通知程序發生了可以預知的異常;必須通過try-catch被顯式地捕獲或者通過throws子句進行傳遞,不可以通過 UncaughtExceptionHandler 處理

Throwable 的序列化

public class Throwable implements Serializable

關於序列化

  • 對象序列化就是把一個對象的狀態轉化成一個字節流
  • 序列化后的對象可以存儲在硬盤上,可以通過網絡傳輸,通過RMI遠程調用等方式傳輸
  • 通過ObjectOutputStreamObjectInputStream對對象進行序列化及反序列化
  • 反序列化后可以重建該對象

為什么Throwable會支持序列化?

  • 參考
  • 異常出現后存儲成日志。
  • 服務端出現錯誤可以通知到客戶端。

官方文檔

Throwable

繼承自:Object
實現的接口:Serializable
直接已知子類:Error, Exception

Throwable 類是 Java 語言中所有錯誤或異常的超類。只有當對象是此類(或其子類之一)的實例時,才能通過 Java 虛擬機或者 Java throw 語句拋出。類似地,只有此類或其子類之一才可以是 catch 子句中的參數類型。

兩個子類的實例,Error 和 Exception,通常用於指示發生了異常情況。通常,這些實例是在異常情況的上下文中新近創建的,因此包含了相關的信息(比如堆棧跟蹤數據)。

Throwable 包含了其線程創建時線程執行堆棧的快照,它還包含了給出有關錯誤更多信息的消息字符串。最后,它還可以包含 cause(原因):另一個導致此 throwable 拋出的 throwable。此 cause 設施在 1.4 版本中首次出現。它也稱為 異常鏈 設施,因為 cause 自身也會有 cause,依此類推,就形成了異常鏈,每個異常都是由另一個異常引起的。

導致 throwable cause 的一個理由是,拋出它的類構建在低層抽象之中,而高層操作由於低層操作的失敗而失敗。 
導致 throwable cause 的另一個理由是,拋出它的方法必須符合通用接口,而通用接口不允許方法直接拋出 cause。

在版本 1.4 中還引入了 getStackTrace() 方法,它允許通過各種形式的 printStackTrace() 方法編程訪問堆棧跟蹤信息,這些信息以前只能以文本形式使用。此信息已經添加到該類的序列化表示形式,因此 getStackTrace 和 printStackTrace 將可在反序列化時獲得的 throwable 上正確操作。

構造方法

Throwable(String message, Throwable cause) //構造一個帶指定詳細消息和 cause 的新 throwable
Throwable(String message)  //Cause未初始化,可在以后通過調用 initCause(Throwable) 來初始化
Throwable(Throwable cause)  //構造一個將 null 作為其詳細消息的新 throwable
Throwable()  //構造一個將 null 作為其詳細消息的新 throwable
  • 注意,與 cause 相關的詳細消息不是自動合並到這個 throwable 的詳細消息中的。調用 fillInStackTrace() 方法來初始化新創建的 throwable 中的堆棧跟蹤數據。
  • 參數:message - 詳細消息。保存此消息,以便以后通過 getMessage() 方法獲取它
  • 參數:cause - 原因。保存此 cause,以便以后通過 getCause() 方法獲取它。允許 null 值,指出 cause 是不存在的或是未知的。

普通 get/set 方法

  • String getMessage() 返回此 throwable 的詳細消息字符串。
  • String getLocalizedMessage() 創建此 throwable 的本地化描述。
    • 子類可以重寫此方法,以便生成特定於語言環境的消息。
    • 對於不重寫此方法的子類,默認實現返回與 getMessage() 相同的結果。
  • String toString() 返回此 throwable 的簡短描述。
    • 如果 getLocalizedMessage 返回 null,則只返回類名稱。
    • 格式【此對象的類的 name: (冒號和一個空格) 調用此對象 getLocalizedMessage() 方法的結果】,如【java.lang.Throwable: 異常信息】
  • Throwable getCause() 返回此 throwable 的 cause;如果 cause 不存在或未知,則返回 null。該 Cause 是導致拋出此 throwable 的throwable。
  • Throwable initCause(Throwable cause) 將此 throwable 的 cause 初始化為指定值。
    • 允許 null 值,指出 cause 是不存在的或是未知的。
    • 此方法至多可以調用一次。
    • 此方法通常從構造方法中調用,或者在創建 throwable 后立即調用。
    • 如果此 throwable 通過 Throwable(Throwable) 或 Throwable(String,Throwable) 創建,此方法一次也不能調用。

操作堆棧跟蹤信息

  • Throwable fillInStackTrace() 在異常堆棧跟蹤中填充。此方法在 Throwable 對象信息中記錄有關當前線程堆棧幀的當前狀態。
  • StackTraceElement[] getStackTrace() 提供編程訪問由 printStackTrace() 輸出的堆棧跟蹤信息。
  • void setStackTrace(StackTraceElement[] stackTrace) 設置將由 getStackTrace() 返回,並由 printStackTrace() 和相關方法輸出的堆棧跟蹤元素。

打印堆棧跟蹤信息

  • void printStackTrace() 將此 throwable 及其追蹤輸出至標准錯誤流System.err。
  • void printStackTrace(PrintStream s) 將此 throwable 及其追蹤輸出到指定的輸出流。
  • void printStackTrace(PrintWriter s) 將此 throwable 及其追蹤輸出到指定的PrintWriter。

注意,不必 重寫任何 PrintStackTrace 方法,應通過調用 getCause 方法來確定 throwable 的 cause。 
通過 printStackTrace 輸出的堆棧跟蹤信息的格式:第一行包含此對象的 toString() 方法的結果,剩余行表示以前由方法 fillInStackTrace() 記錄的數據。

Error

Error 是 Throwable 的子類,用於指示合理的應用程序不應該試圖捕獲嚴重問題。大多數這樣的錯誤都是異常條件。雖然 ThreadDeath 錯誤是一個“正規”的條件,但它也是 Error 的子類,因為大多數應用程序都不應該試圖捕獲它。

在執行該方法期間,無需在其 throws 子句中聲明可能拋出但是未能捕獲的 Error 的任何子類,因為這些錯誤【可能是再也不會發生】的異常條件

常見的直接子類:

  • OutOfMemoryError:因為內存溢出或沒有可用的內存提供給垃圾回收器時,Java 虛擬機無法分配一個對象,這時拋出該異常。
  • StackOverflowError:當應用程序遞歸太深而發生堆棧溢出時,拋出該錯誤。
  • NoClassDefFoundError:當 Java 虛擬機或 ClassLoader 實例試圖在類的定義中加載,但無法找到該類的定義時,拋出此異常。
  • UnsatisfiedLinkError:當 Java 虛擬機無法找到聲明為 native 的方法的適當本地語言定義時,拋出該錯誤。
  • IOError:當發生嚴重的 I/O 錯誤時,拋出此錯誤。
  • LinkageError:LinkageError 的子類指示一個類在一定程度上依賴於另一個類;但是,在編譯前一個類之后,后一個類發生了不相容的改變。
  • ClassCircularityError:當初始化類時檢測到類的循環調用的時候,拋出該錯誤。
  • ClassFormatError:當 Java 虛擬機試圖讀取類文件並確定該文件存在格式錯誤或無法解釋為類文件時,拋出該錯誤。
  • ExceptionInInitializerError:靜態初始化程序中發生意外異常的信號。拋出此錯誤表明在計算靜態初始值或靜態變量的初始值期間發生異常。
  • IncompatibleClassChangeError:在某些類定義中出現不兼容的類更改時拋出該異常。某些目前執行的方法所依賴的類定義已發生了變化。
  • VerifyError:當“校驗器”檢測到一個類文件雖然格式正確,但包含着一些內部不一致性或安全性問題時,拋出該錯誤。
  • ThreadDeath:調用 Thread 類中帶有零參數的 stop 方法時(此方法已過時),受害線程將拋出一個 ThreadDeath 實例。僅當應用程序在被異步終止后必須清除時才應該捕獲這個類的實例。
  • VirtualMachineError:當 Java 虛擬機崩潰或用盡了它繼續操作所需的資源時,拋出該錯誤。
  • InternalError:該異常指示 Java 虛擬機中出現一些意外的內部錯誤。
  • UnknownError:當 Java 虛擬機中出現一個未知但嚴重的異常時,拋出該錯誤。

Exception

Exception 類及其子類是 Throwable 的一種形式,它指出了合理的應用程序想要捕獲的條件。

直接已知子類:

AclNotFoundException, ActivationException, AlreadyBoundException, ApplicationException, AWTException, BackingStoreException, BadAttributeValueExpException, BadBinaryOpValueExpException, BadLocationException, BadStringOperationException, BrokenBarrierException, CertificateException, ClassNotFoundException, CloneNotSupportedException, DataFormatException, DatatypeConfigurationException, DestroyFailedException, ExecutionException, ExpandVetoException, FontFormatException, GeneralSecurityException, GSSException, IllegalAccessException, IllegalClassFormatException, InstantiationException, InterruptedException, IntrospectionException, InvalidApplicationException, InvalidMidiDataException, InvalidPreferencesFormatException, InvalidTargetObjectTypeException, InvocationTargetException, IOException, JAXBException, JMException, KeySelectorException, LastOwnerException, LineUnavailableException, MarshalException, MidiUnavailableException, MimeTypeParseException, MimeTypeParseException, NamingException, NoninvertibleTransformException, NoSuchFieldException, NoSuchMethodException, NotBoundException, NotOwnerException, ParseException, ParserConfigurationException, PrinterException, PrintException, PrivilegedActionException, PropertyVetoException, RefreshFailedException, RemarshalException, RuntimeException, SAXException, ScriptException, ServerNotActiveException, SOAPException, SQLException, TimeoutException, TooManyListenersException, TransformerException, TransformException, UnmodifiableClassException, UnsupportedAudioFileException, UnsupportedCallbackException, UnsupportedFlavorException, UnsupportedLookAndFeelException, URIReferenceException, URISyntaxException, UserException, XAException, XMLParseException, XMLSignatureException, XMLStreamException, XPathException

RuntimeException

RuntimeException 是那些可能在 Java 虛擬機正常運行期間拋出的異常的超類。 
可能在執行方法期間拋出但未被捕獲的 RuntimeException 的任何子類都無需在【throws】子句中進行聲明

直接已知子類:

AnnotationTypeMismatchException
ArithmeticException
ArrayStoreException
BufferOverflowException
BufferUnderflowException
CannotRedoException
CannotUndoException
ClassCastException
CMMException
ConcurrentModificationException
DOMException
EmptyStackException
EnumConstantNotPresentException
EventException
IllegalArgumentException
IllegalMonitorStateException
IllegalPathStateException
IllegalStateException
ImagingOpException
IncompleteAnnotationException
IndexOutOfBoundsException
JMRuntimeException
LSException
MalformedParameterizedTypeException
MirroredTypeException
MirroredTypesException
MissingResourceException
NegativeArraySizeException
NoSuchElementException
NoSuchMechanismException
NullPointerException
ProfileDataException
ProviderException
RasterFormatException
RejectedExecutionException
SecurityException
SystemException
TypeConstraintException
TypeNotPresentException
UndeclaredThrowableException
UnknownAnnotationValueException
UnknownElementException
UnknownTypeException
UnmodifiableSetException
UnsupportedOperationException
WebServiceException

Checked 異常和 Unchecked 異常

面試題:兩者的區別

面試題:NullPointerExceptionIOException有什么區別? 
答:

  • 這兩個是 Java 中的兩種異常,一個是 Unchecked Exceptione,一個是 Checked Exceptione
  • Checked異常繼承java.lang.Exception類,Checked異常必須通過try-catch被顯式地捕獲或者通過throws子句進行傳遞。
  • Unchecked異常即運行時異常,繼承自java.lang.RuntimeException類,是那些可能在 Java 虛擬機正常運行期間拋出的異常,unchecked 異常可以既不必捕獲也不拋出

運行時異常 
運行時異常我們一般不處理,當出現這類異常的時候程序會由 JVM 接管。比如,我們從來沒有去處理過 NullPointerException,而且這個異常還是最常見的異常之一。

出現運行時異常的時候,程序會將異常一直向上拋,一直拋到遇到處理代碼,如果沒有 catch 塊進行處理,到了最上層,如果是多線程就有 Thread.run() 拋出,如果不是多線程那么就由 main.run() 拋出。拋出之后,如果是線程,那么該線程也就終止了,如果是主程序,那么該程序也就終止了。

其實運行時異常的也是繼承自 Exception,也可以用 catch 塊對其處理,只是我們一般不處理罷了,也就是說,如果不對運行時異常進行 catch 處理,那么結果不是線程退出就是主程序終止。如果不想終止,那么我們就必須捕獲所有可能出現的運行時異常。

兩種異常的使用場景

  • Checked和unchecked異常從功能的角度來講是等價的,可以用checked異常實現的功能必然也可以用unchecked異常實現,反之亦然。
  • 選擇checked異常還是unchecked異常是個人習慣或者組織規定問題。並不存在誰比誰強大的問題。
  • Unchecked異常避免了不必要的try-catch塊,不會使代碼顯得雜亂;Unchecked異常不會因為異常聲明聚集使方法聲明顯得雜亂。

checked 異常使用案例

public class Test {
    public static void main(String[] args) {

        try {
            new Test().testException();
        } catch (MyException e) {
            e.printStackTrace();
            System.out.println("調用拋出checked異常的方法時,同樣必須通過try-catch顯式地捕獲或者通過throws子句進行傳遞");
        }
    }

    void testException() throws MyException {
        throw new MyException("拋出checked異常時,必須通過try-catch顯式地捕獲或者通過throws子句進行傳遞");
    }

    class MyException extends Exception {
        MyException(String s) {
            super(s);
        }
    }
}

unchecked 異常使用案例

和上面相比,只需把自定義的異常由繼承自Exception改為繼承自RuntimeException即可。

由於RuntimeException繼承自Exception,所以修改后上面其他代碼都不需要改變就可以正常使用。但RuntimeException可以即不必捕獲也不拋出:

public class Test {
    public static void main(String[] args) {
        new Test().testException();
    }

    void testException() {
        throw new MyException("unchecked異常可以即不必捕獲也不拋出");
    }

    class MyException extends RuntimeException {
        MyException(String s) {
            super(s);
        }
    }
}

checked 異常錯誤使用案例

public class Test {

    public static void main(String[] args) {
        try {
            start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void start() {
        System.out.println("這個方法並沒有聲明會拋出checked 異常,例如IOException");
    }
}

上面的代碼編譯是通不過的:

因為IOException是checked異常,而start方法並沒有拋出IOException,編譯器將在處理IOException時報錯。

但是如果你將IOException改為Exception,編譯器報錯將消失,因為Exception可以用來捕捉所有運行時異常(包括unchecked異常),這樣就不需要聲明拋出語句。

將上例中的 IOException 改為 unchecked 異常也是可以的,例如改為 NullPointerException

其他小知識點

error和exception有什么區別

  • error表示系統級的錯誤,是java運行環境內部錯誤或者硬件問題,不能指望程序來處理這樣的問題,除了退出運行外別無選擇,它是Java虛擬機拋出的。
  • exception 表示程序需要捕捉、需要處理的異常,是由於程序設計的不完善而出現的問題,程序必須處理的問題。

final、finally、finalize的區別

  • final用於聲明變量、方法和類的,分別表示變量值不可變,方法不可覆蓋,類不可以繼承
  • finally是異常處理中的一個關鍵字,表示finally{}里面的代碼一定要執行
  • finalize是Object類的一個方法,在垃圾回收的時候會調用被回收對象的此方法。

常見的Exception和Error有哪些

  • 常見的 Checked 異常:ClassNotFoundExceptionCloneNotSupportedException,DataFormatException,IllegalAccessException,InterruptedExceptionIOExceptionNoSuchFieldExceptionNoSuchMethodException,ParseException,TimeoutException,XMLParseException
  • 常見的 Unchecked 異常:BufferOverflowException,ClassCastExceptionIllegalArgumentException,IllegalStateException,IndexOutOfBoundsException,NoSuchElementException,NullPointerException,SecurityException,SystemException,UnsupportedOperationException
  • 常見的 Error:OutOfMemoryErrorStackOverflowError、NoClassDefFoundError、UnsatisfiedLinkError、IOErrorThreadDeath、ClassFormatError、InternalError、UnknownError

使用 try-catch 會影響性能嗎?

  • 無異常發生時,使用try-catch語句影響不大
  • 有異常發生相比無異常發生,處理時長更長
  • printStackTrace()很耗時

2018-06-09


免責聲明!

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



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