精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

Java中的異常處理:高級特性和類型

譯文
開發 前端
Java異常處理的高級特性包括堆棧跟蹤、異常鏈、Try-with-resources、多重捕獲、最終重新拋出異常,以及堆棧遍歷。

譯者 | 李睿

審校 | 重樓

Java平臺包含了多種語言特性和庫類型,用于處理與預期程序行為不同的異常。本文將介紹Java中異常處理的高級特性,其中包括堆棧跟蹤(stack traces)、異常鏈(exception chaining)、try-with-resources重捕獲multi-catch)、最終重新拋出異常final re-throw,以及堆棧遍歷(stack walking)。

Java教程的學習內容

以下將介紹在Java程序中處理異常的高級特性:

  • 堆棧跟蹤和堆棧跟蹤元素
  • 原因(Causes)和異常鏈exception chaining)
  • Try-with-resources
  • 多重捕獲(multi-catch)
  • 最終重新拋出異常final re-throw
  • StackWalking和StackWalking API

使用堆棧跟蹤進行異常處理

每個JVM線程(執行路徑)都與創建線程時創建的堆棧相關聯。這個數據結構被劃分為幀(frame),這些幀是與方法調用相關聯的數據結構。因此,每個線程的堆棧通常被稱為方法調用堆棧。

每當調用一個方法時,就會創建一個新的幀。每幀存儲局部變量、參數變量(保存傳遞給方法的參數)、返回到調用方法的信息、存儲返回值的空間、在調度異常時有用的信息等等。

堆棧跟蹤是在線程執行過程中某個時間點的活動堆棧幀的報告。Java的Throwable類(在Java.lang包中)提供了打印堆棧跟蹤、填充堆棧跟蹤和訪問堆棧跟蹤元素的方法。

下載本教程中示例的源代碼

打印堆棧跟蹤

當throw語句拋出一個throwable時,它首先會在當前執行的方法中查找一個合適的catch塊。如果沒有找到,它會回溯方法調用棧,尋找能夠處理該異常的最近的catch塊。如果仍然沒有找到,JVM將終止執行,并顯示一條合適的消息。可以在清單1中看到這一行為。

清單1.PrintStackTraceDemo.java (version 1)

import java.io.IOException;
public class PrintStackTraceDemo
{
 public static void main(String[] args) throws IOException
 {
 throw new IOException();
 }
}

清單1的示例創建了一個java.io.IOException對象,并將該對象拋出main()方法。因為main()不處理這個throwable,而且main()是頂級方法,因此JVM會終止執行,并顯示一條合適的消息。對于這個應用程序,將看到以下消息:

Exception in thread "main" java.io.IOException
 at PrintStackTraceDemo.main(PrintStackTraceDemo.java:7)

JVM通過調用Throwable的void printStackTrace()方法輸出這條消息,該方法在標準錯誤流上打印調用throwable對象的堆棧跟蹤。輸出的第一行顯示了調用throwable對象的toString()方法的結果。下一行顯示了fillInStackTrace()之前記錄的數據(稍后討論)。

其他的打印堆棧跟蹤方法

throwable的重載void printStackTrace(PrintStream ps)和void printStackTrace(printwwriter pw)方法將堆棧跟蹤輸出到指定的流或寫入器。

堆棧跟蹤顯示了創建throwable的源文件和行號。在本例中,它是在PrintStackTrace.java源文件的第7行創建的。

可以直接調用printStackTrace(),通常是調用catch塊。例如,考慮PrintStackTraceDemo應用程序的第二個版本。

清單2. PrintStackTraceDemo.java (version 2)

import java.io.IOException;
public class PrintStackTraceDemo
{
 public static void main(String[] args) throws IOException
 {
 try
 {
 a();
 }
 catch (IOException ioe)
 {
 ioe.printStackTrace();
 }
 }
 static void a() throws IOException
 {
 b();
 }
 static void b() throws IOException
 {
 throw new IOException();
 }
}

清單2展示了一個main()方法,它調用方法a(),而方法a()又調用方法b()。方法b()向JVM拋出一個IOException對象,JVM將展開方法調用堆棧,直到找到main()的catch塊,該塊可以處理異常。異常是通過對throwable調用printStackTrace()來處理的。這種方法產生以下輸出:

java.io.IOException
 at PrintStackTraceDemo.b(PrintStackTraceDemo.java:24)
 at PrintStackTraceDemo.a(PrintStackTraceDemo.java:19)
 at PrintStackTraceDemo.main(PrintStackTraceDemo.java:9)

printStackTrace()不會輸出線程的名稱。與其相反,它首先會在第一行調用 Throwable 對象的 toString() 方法,以返回異常的完全限定類名(如 java.io.IOException),這是輸出的第一部分然后輸出方法調用層次結構:最近調用的方法(b())位于頂部,main()位于底部。

堆棧跟蹤標識的是哪一行?

堆棧跟蹤標識了創建throwable的行,但它不會標識拋出throwable的行(通過throw),除非拋出和創建是在同一行代碼中完成的。

填充堆棧跟蹤

throwable聲明了一個Throwable fillInStackTrace()方法來填充執行堆棧跟蹤。在調用Throwable對象中,它記錄有關當前線程堆棧幀的當前狀態的信息。考慮清單3。

清單3. FillInStackTraceDemo.java (version 1)

import java.io.IOException;
public class FillInStackTraceDemo
{
 public static void main(String[] args) throws IOException
 {
 try
 {
 a();
 }
 catch (IOException ioe)
 {
 ioe.printStackTrace();
 System.out.println();
 throw (IOException) ioe.fillInStackTrace();
 }
 }
 static void a() throws IOException
 {
 b();
 }
 static void b() throws IOException
 {
 throw new IOException();
 }
}

清單3和清單2的主要區別在于catch塊的throw (IOException) ioe.fillInStackTrace();聲明。該語句替換ioe的堆棧跟蹤,然后重新拋出throwable。應該看到這樣的輸出:

java.io.IOException
 at FillInStackTraceDemo.b(FillInStackTraceDemo.java:26)
 at FillInStackTraceDemo.a(FillInStackTraceDemo.java:21)
 at FillInStackTraceDemo.main(FillInStackTraceDemo.java:9)
Exception in thread "main" java.io.IOException
 at FillInStackTraceDemo.main(FillInStackTraceDemo.java:15)

第二個堆棧跟蹤顯示ioe.fillInStackTrace()的位置,而不是重復標識IOException對象創建位置的初始堆棧跟蹤。

Throwable構造函數和fillInStackTrace()

throwable的每個構造函數都調用fillInStackTrace()。然而,當將false傳遞給writableStackTrace時,下面的構造函數(在JDK 7中引入)不會調用這個方法:

Throwable(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace)

fillInStackTrace()調用一個原生方法,該方法沿著當前線程的方法調用堆棧遍歷以構建堆棧跟蹤。這種遍歷代價高昂,如果過于頻繁,可能會影響性能。

如果遇到性能非常關鍵的情況(可能涉及嵌入式設備),可以通過重寫fillInStackTrace()來阻止堆棧跟蹤的構建。請查看清單4。

清單4. FillInStackTraceDemo.java (version 2)

{
 public static void main(String[] args) throws NoStackTraceException
 {
 try
 {
 a();
 }
 catch (NoStackTraceException nste)
 {
 nste.printStackTrace();
 }
 }
 static void a() throws NoStackTraceException
 {
 b();
 }
 static void b() throws NoStackTraceException
 {
 throw new NoStackTraceException();
 }
}
class NoStackTraceException extends Exception
{
 @Override
 public synchronized Throwable fillInStackTrace()
 {
 return this;
 }
}

清單4引入了NoStackTraceException。這個自定義檢查的異常類重寫fillInStackTrace()以返回This——對調用Throwable的引用。該程序生成以下輸出:

NoStackTraceException

注釋掉覆蓋的fillInStackTrace()方法,會看到以下輸出:

NoStackTraceException
 at FillInStackTraceDemo.b(FillInStackTraceDemo.java:22)
 at FillInStackTraceDemo.a(FillInStackTraceDemo.java:17)
 at FillInStackTraceDemo.main(FillInStackTraceDemo.java:7)

訪問堆棧跟蹤的元素

有時,需要訪問堆棧跟蹤的元素,以便提取日志記錄、識別資源泄漏源和其他目的所需的詳細信息。printStackTrace()和fillInStackTrace()方法不支持這個任務,但是java.lang.StackTraceElement和它的方法就是為這個任務設計的。

stacktraceelement類描述了在堆棧跟蹤中表示堆棧幀的元素。它的方法可用于返回類的完全限定名,該類包含這個堆棧跟蹤元素所表示的執行點以及其他有用信息。以下是該類的主要方法:

  • String getClassName()返回包含這個堆棧跟蹤元素所表示的執行點的類的完全限定名。
  • String getFileName()返回包含這個堆棧跟蹤元素所表示的執行點的源文件的名稱。
  • int getLineNumber()返回包含這個堆棧跟蹤元素所表示的執行點的源行的行號。
  • String getMethodName()返回包含這個堆棧跟蹤元素所表示的執行點的方法的名稱。
  • 當包含這個堆棧跟蹤元素所表示的執行點的方法是原生方法時,boolean isNativeMethod() 返回true。

另一個重要方法是java.lang.Thread和Throwable類上的StackTraceElement[]getStackTrace()。這個方法分別返回一個堆棧跟蹤元素數組,表示調用線程的堆棧轉儲,并提供對printStackTrace()打印的堆棧跟蹤信息的程序化訪問。

清單5展示了StackTraceElement和getStackTrace()。

清單5. StackTraceElementDemo.java (version 1)

import java.io.IOException;
public class StackTraceElementDemo
{
 public static void main(String[] args) throws IOException
 {
 try
 {
 a();
 }
 catch (IOException ioe)
 {
 StackTraceElement[] stackTrace = ioe.getStackTrace();
 for (int i = 0; i < stackTrace.length; i++)
 {
 System.err.println("Exception thrown from " + 
 stackTrace[i].getMethodName() + " in class " + 
 stackTrace[i].getClassName() + " on line " + 
 stackTrace[i].getLineNumber() + " of file " + 
 stackTrace[i].getFileName());
 System.err.println();
 }
 }
 }
 static void a() throws IOException
 {
 b();
 }
 static void b() throws IOException
 {
 throw new IOException();
 }
}

當運行這個應用程序時,會看到以下輸出:

Exception thrown from b in class StackTraceElementDemo on line 33 of file StackTraceElementDemo.java
Exception thrown from a in class StackTraceElementDemo on line 28 of file StackTraceElementDemo.java
Exception thrown from main in class StackTraceElementDemo on line 9 of file StackTraceElementDemo.java

最后,在Throwable類上有setStackTrace()方法。這種方法是為遠程過程調用(RPC)框架和其他高級系統設計的,允許客戶端覆蓋在構造Throwable時由fillInStackTrace()生成的默認堆棧跟蹤。

之前展示了如何重寫fillInStackTrace()以防止構建堆棧跟蹤。然而,可以使用StackTraceElement和setStackTrace()來安裝新的堆棧跟蹤。可以創建一個通過以下構造函數初始化的StackTraceElement對象數組,并將該數組傳遞給setStackTrace():

StackTraceElement(String declaringClass, String methodName, String fileName, int lineNumber)

清單6演示了StackTraceElement和setStackTrace()。

清單6 . StackTraceElementDemo.java (version 2)

public class StackTraceElementDemo
{
 public static void main(String[] args) throws NoStackTraceException
 {
 try
 {
 a();
 }
 catch (NoStackTraceException nste)
 {
 nste.printStackTrace();
 }
 }
 static void a() throws NoStackTraceException
 {
 b();
 }
 static void b() throws NoStackTraceException
 {
 throw new NoStackTraceException();
 }
}
class NoStackTraceException extends Exception
{
 @Override
 public synchronized Throwable fillInStackTrace()
 {
 setStackTrace(new StackTraceElement[]
 {
 new StackTraceElement("*StackTraceElementDemo*", 
 "b()",
 "StackTraceElementDemo.java",
 22),
 new StackTraceElement("*StackTraceElementDemo*", 
 "a()",
 "StackTraceElementDemo.java",
 17),
 new StackTraceElement("*StackTraceElementDemo*", 
 "main()",
 "StackTraceElementDemo.java",
 7)
 });
 return this;
 }
}

采用星號包圍了StackTraceElementDemo類名,以證明這個堆棧跟蹤是輸出的跟蹤。運行應用程序,將觀察到以下堆棧跟蹤:

NoStackTraceException
 at *StackTraceElementDemo*.b()(StackTraceElementDemo.java:22)
 at *StackTraceElementDemo*.a()(StackTraceElementDemo.java:17)
 at *StackTraceElementDemo*.main()(StackTraceElementDemo.java:7)

原因和異常鏈

在異常處理中,一個catch塊經常會通過拋出另一個異常來響應捕獲到的異常。第一個異常被認為是導致第二個異常發生的原因,這兩個異常被隱式地鏈接在一起。

例如,調用庫方法的catch塊時可能會出現內部異常,而該內部異常對標準庫方法的調用者不應該是可見的。因為需要通知調用者出現了錯誤,所以catch塊會創建一個符合庫方法合約接口的外部異常,并且調用者可以處理這個異常。

由于內部異常可能對于診斷問題非常有幫助,catch塊應該將內部異常包裝在外部異常中。被包裝的異常被稱為原因(cause),因為它的存在導致拋出外部異常。此外,包裝的內部異常(原因)被顯式地鏈接到外部異常。

對原因和異常鏈的支持是通過兩個Throwable構造函數(以及對應的exception、RuntimeException和Error)和兩種方法提供的:

  • Throwable(String message, Throwable cause)
  • Throwable(Throwable cause)
  • Throwable getCause()
  • Throwable initCause(Throwable cause)

Throwable構造函數(及其對應的子類)允許在構造Throwable時包裝原因。如果處理的教學法遺留代碼的自定義異常類不支持任何一個構造函數,可以通過在Throwable上調用initCause()來包裝原因。需要注意的是,initCause()只能被調用一次。無論哪種方式,都可以通過調用getCause()返回原因。當沒有原因時,這個方法返回null。

清單7 展示了一個名為 CauseDemo 的應用程序,該應用程序演示了異常原因(以及異常鏈)的概念。

清單7. CauseDemo.java

public class CauseDemo
{
 public static void main(String[] args)
 {
 try
 {
 Library.externalOp();
 } 
 catch (ExternalException ee)
 {
 ee.printStackTrace();
 }
 }
}
class Library
{
 static void externalOp() throws ExternalException
 {
 try
 {
 throw new InternalException();
 } 
 catch (InternalException ie)
 {
 throw (ExternalException) new ExternalException().initCause(ie);
 }
 }
 private static class InternalException extends Exception
 {
 }
}
class ExternalException extends Exception
{
}

清單7顯示了CauseDemo、Library和ExternalException類。CauseDemo的main()方法調用Library的externalOp()方法并catch其拋出的ExternalException對象。catch塊調用printStackTrace()來輸出外部異常及其原因。

庫的externalOp()方法故意拋出一個InternalException對象,其catch塊將該對象映射到一個ExternalException對象。因為ExternalException不支持可以接受cause參數的構造函數,所以使用initCause()來包裝InternalException對象。

運行這個應用程序,就會看到下面的堆棧跟蹤:

ExternalException
 at Library.externalOp(CauseDemo.java:26)
 at CauseDemo.main(CauseDemo.java:7)
Caused by: Library$InternalException
 at Library.externalOp(CauseDemo.java:22)
 ... 1 more

第一個堆棧跟蹤顯示,外部異常起源于Library的externalOp()方法(CauseDemo.java中的第26行),并在第7行對該方法的調用中拋出。第二個堆棧跟蹤顯示了內部異常原因源自Library的externalOp()方法(CauseDemo.java中的第22行)。... 1 more行表示第一個堆棧跟蹤的最后一行。如果可以刪除這一行,將看到以下輸出:

ExternalException
 at Library.externalOp(CauseDemo.java:26)
 at CauseDemo.main(CauseDemo.java:7)
Caused by: Library$InternalException
 at Library.externalOp(CauseDemo.java:22)
 at CauseDemo.main(CauseDemo.java:7)

可以通過改變ee.printStackTrace(); to來證明第二個跟蹤的最后一行是第一個堆棧跟蹤的最后一行的副本。

ee.getCause().printStackTrace();.

關于...more”以及探究原因鏈的更多信息

通常,...n more這樣的表述意味著原因的堆棧跟蹤的最后n行與前一個堆棧跟蹤的最后n行是重復的。

這個例子只揭示了一個原因。然而,從非簡單的現實世界應用程序中拋出的異常可能包含由多個原因構成的復雜鏈。可以通過使用如下所示的循環來訪問這些原因:

catch (Exception exc)
{
 Throwable t = exc.getCause();
 while (t != null)
 {
 System.out.println(t);
 t = t.getCause();
 }
}

Try-with-resources

Java應用程序經常訪問文件、數據庫連接、套接字和其他依賴于相關系統資源的資源(例如文件句柄)。系統資源的稀缺性意味著它們最終必須被釋放,即使在發生異常時也是如此。當系統資源沒有被釋放,應用程序在嘗試獲取其他資源時最終會失敗,因為沒有更多相關的系統資源可用。

在對異常處理基礎的介紹中,提到資源(實際上是它們所依賴的系統資源)在finally塊中釋放。這可能會導致冗長的樣板代碼,例如下面顯示的文件關閉代碼:

finally
{
 if (fis != null)
 try
 {
 fis.close();
 }
 catch (IOException ioe)
 {
 // ignore exception
 }
 if (fos != null)
 try
 {
 fos.close();
 }
 catch (IOException ioe)
 {
 // ignore exception
 }
}

這個樣板代碼不僅增加了類文件的容量,而且編寫它的單調乏味可能會導致錯誤,甚至可能無法關閉文件。JDK 7引入了try-with-resource”來克服這個問題。

try-with-resource的基本原理

當執行離開打開和使用資源的范圍時,try-with-resources構造會自動關閉打開的資源,無論是否從該范圍拋出異常。這個構造的語法如下:

try (resource acquisitions)
{
 // resource usage
}

try關鍵字由分號分隔的資源獲取語句列表參數化,其中每條語句獲取一個資源。每個獲取的資源都可用于try塊的主體,并在執行離開該主體時自動關閉。與常規的try語句不同,try-with-resource不需要catch塊和/或finally塊來跟隨try(),盡管它們可以指定。

考慮以下面向文件的示例:

try (FileInputStream fis = new FileInputStream("abc.txt"))
{
 // Do something with fis and the underlying file resource.
}

在這個例子中,獲取了底層文件資源(abc.txt)的輸入流。try塊使用這個資源執行某些操作,流(以及文件)在退出try塊時關閉。

將“var”與“try-with-resource”一起使用

JDK 10引入了對var的支持,var是一種具有特殊含義的標識符(即不是關鍵字)。可以使用var和try with資源來減少樣板。例如,可以將前面的示例簡化為以下內容:

try (var fis = new FileInputStream("abc.txt"))
{
 // Do something with fis and the underlying file resource.
}

try-with-resource場景中復制文件

本文作者從文件復制應用程序中摘錄了copy()方法。這種方法的finally塊包含前面介紹的文件關閉樣板。清單8通過使用try-with-resources處理清理,從而改進這種方法。

清單8. Copy.java

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class Copy
{
 public static void main(String[] args)
 {
 if (args.length != 2)
 {
 System.err.println("usage: java Copy srcfile dstfile");
 return;
 }
 try
 {
 copy(args[0], args[1]);
 }
 catch (IOException ioe)
 {
 System.err.println("I/O error: " + ioe.getMessage());
 }
 }
 static void copy(String srcFile, String dstFile) throws IOException
 {
 try (FileInputStream fis = new FileInputStream(srcFile);
 FileOutputStream fos = new FileOutputStream(dstFile))
 {
 int c;
 while ((c = fis.read()) != -1)
 fos.write(c);
 }
 }
}

copy()使用try-with-resources來管理源文件和目標文件資源。下面的圓括號代碼嘗試創建這些文件的文件輸入和輸出流。假設成功,它的主體將執行,將源文件復制到目標文件。

無論是否拋出異常,try-with-resources都能確保在執行離開try塊時關閉這兩個文件。因為不需要前面顯示的樣板文件關閉代碼,所以清單8的copy()方法要簡單得多,也更容易閱讀。

設計資源類以支持try-with-resources

try-with-resources構造要求資源類實現java.lang.Closeable接口或JDK 7引入的java.lang.AutoCloseable超級接口。Java7之前的類(如Java.io.FileInputStream)實現了Closeable接口,它提供了一個拋出IOException或子類的void close()方法。

從Java 7開始,類可以實現AutoCloseable,其單個void close()方法可以拋出Java.lang.Exception或子類。throws子句已經擴展,以適應可能需要添加close()方法的情況,這些方法可以在IOException層次結構之外拋出異常;例如java.sql.SQLException

清單9顯示了一個CustomARM應用程序,它展示了如何配置自定義資源類,以便可以在try-with-resources場景中使用它。

清單9. CustomARM.java

public class CustomARM
{
 public static void main(String[] args)
 {
 try (USBPort usbp = new USBPort())
 {
 System.out.println(usbp.getID());
 }
 catch (USBException usbe)
 {
 System.err.println(usbe.getMessage());
 }
 }
}

class USBPort implements AutoCloseable
{
 USBPort() throws USBException
 {
 if (Math.random() < 0.5)
 throw new USBException("unable to open port");
 System.out.println("port open");
 }

 @Override
 public void close()
 {
 System.out.println("port close");
 }

 String getID()
 {
 return "some ID";
 }
}

class USBException extends Exception
{
 USBException(String msg)
 {
 super(msg);
 }
}

清單9模擬了一個USB端口,可以打開和關閉該端口,并返回端口的 ID。在構造函數中,使用了 Math.random() 來模擬可能拋出異常的情況,以便可以觀察到 try-with-resources 語句在異常被拋出或未拋出時的行為。

編譯這個清單并運行應用程序。如果端口打開,將看到以下輸出:

port open
some ID
port close

如果端口關閉,將看到以下輸出:

unable to open port

在try-with-resources中抑制異常

如果你有一定的編程經驗,可能會注意到try-with-resources結構中存在一個潛在的問題:假設try塊拋出了一個異常。這個結構通過調用資源對象的close()方法來關閉資源以出響應。然而,close()方法本身也可能拋出異常。

當close()方法拋出異常時(例如,FileInputStream的void close()方法可以拋出IOException),這個異常會掩蓋或隱藏原始異常。這看起來像是原始的異常丟失了。

實際上,情況并非如此:try-with-resources會抑制close()拋出的異常。它還通過調用java.lang.Throwable的void addSuppressed(Throwable exception)方法,將異常添加到原始異常的抑制異常數組中。

清單10展示了一個SupExDemo應用程序,它演示了如何在try-with-resources的場景中抑制異常。

清單10. SupExDemo.java

import java.io.Closeable;
import java.io.IOException;
public class SupExDemo implements Closeable
{
 @Override
 public void close() throws IOException
 {
 System.out.println("close() invoked");
 throw new IOException("I/O error in close()");
 }
 public void doWork() throws IOException
 {
 System.out.println("doWork() invoked");
 throw new IOException("I/O error in work()");
 }
 public static void main(String[] args) throws IOException
 {
 try (SupExDemo supexDemo = new SupExDemo())
 {
 supexDemo.doWork();
 }
 catch (IOException ioe)
 {
 ioe.printStackTrace();
 System.out.println();
 System.out.println(ioe.getSuppressed()[0]);
 }
 }
}

清單10的doWork()方法拋出IOException來模擬某種I/O錯誤。close()方法還會拋出IOException,但這個異常被抑制,這樣它就不會掩蓋doWork()的異常。

catch塊通過調用Throwable的Throwable[]getSuppressed()方法來訪問被抑制的異常(從close()拋出的異常),該方法返回一個包含被抑制異常的數組。由于在這個例子中只有一個異常被抑制,所以只訪問了數組的第一個元素。

編譯清單10并運行應用程序。應該觀察以下輸出:

doWork() invoked
close() invoked
java.io.IOException: I/O error in work()
 at SupExDemo.doWork(SupExDemo.java:16)
 at SupExDemo.main(SupExDemo.java:23)
 Suppressed: java.io.IOException: I/O error in close()
 at SupExDemo.close(SupExDemo.java:10)
 at SupExDemo.main(SupExDemo.java:24)
java.io.IOException: I/O error in close()

多重捕獲塊(multi-catch)

從JDK 7開始,可以在單個catch塊中捕獲多種類型的異常。這種多重捕獲特性的目的是減少代碼重復,并減少捕獲過于寬泛的異常(例如,catch (Exception e))的誘惑。

假設開發了一個應用程序,可以靈活地將數據復制到數據庫或文件中。清單11展示了一個CopyToDatabaseOrFile類,該類模擬了這種情況,并演示了catch塊代碼重復的問題。

清單11.CopyToDatabaseOrFile.java

import java.io.IOException;
import java.sql.SQLException;
public class CopyToDatabaseOrFile
{
 public static void main(String[] args)
 {
 try
 {
 copy();
 }
 catch (IOException ioe)
 {
 System.out.println(ioe.getMessage());
 // additional handler code
 }
 catch (SQLException sqle)
 {
 System.out.println(sqle.getMessage());
 // additional handler code that's identical to the previous handler's
 // code
 }
 }
 static void copy() throws IOException, SQLException
 {
 if (Math.random() < 0.5)
 throw new IOException("cannot copy to file");
 else
 throw new SQLException("cannot copy to database");
 }
}

JDK 7通過允許在catch塊中指定多個異常類型來克服代碼重復問題,其中每個連續的類型都通過在這些類型之間放置豎線(|)與其前一個類型分開:

try
{
 copy();
}
catch (IOException | SQLException iosqle)
{
 System.out.println(iosqle.getMessage());
}

現在,當copy()拋出異常時,異常將被捕獲并由catch塊處理。

當在catch塊的頭文件中列出多個異常類型時,該參數被隱式地視為final。因此,不能更改參數的值。例如,不能更改存儲在前一個代碼片段的iosqle參數中的引用。

縮減字節碼

編譯處理多種異常類型的catch塊所產生的字節碼將比編譯每個只處理列出的一種異常類型的幾個catch塊要小。處理多種異常類型的catch塊在編譯期間不會產生重復的字節碼。換句話說,字節碼不包含復制的異常處理程序。

最終重新拋出異常

從JDK 7開始,Java編譯器能夠比之前的Java版本更精確地分析被重拋的異常。這個特性僅在沒有對被重拋的異常的catch塊參數進行賦值時有效,該參數被認為是有效的final。當前面的try塊拋出一個屬于參數類型的超類型/子類型的異常時,編譯器會拋出捕獲的異常的實際類型,而不是拋出參數的類型(就像以前的Java版本那樣)。

這個最終重拋異常特性的目的是為了方便在代碼塊周圍添加try-catch語句來攔截、處理并重拋異常,同時不影響從代碼中靜態確定的異常集。此外,這個特性允許在異常被拋出的地方附近提供一個通用的異常處理器來部分處理異常,并在其他地方提供更精確的處理程序來處理重拋的異常。考慮清單12。

清單12.MonitorEngine.java

class PressureException extends Exception
{
 PressureException(String msg)
 {
 super(msg);
 }
}
class TemperatureException extends Exception
{
 TemperatureException(String msg)
 {
 super(msg);
 }
}
public class MonitorEngine
{
 public static void main(String[] args)
 {
 try
 {
 monitor();
 }
 catch (Exception e)
 {
 if (e instanceof PressureException)
 System.out.println("correcting pressure problem");
 else
 System.out.println("correcting temperature problem");
 }
 }
 static void monitor() throws Exception
 {
 try
 {
 if (Math.random() < 0.1)
 throw new PressureException("pressure too high");
 else
 if (Math.random() > 0.9)
 throw new TemperatureException("temperature too high");
 else
 System.out.println("all is well");
 }
 catch (Exception e)
 {
 System.out.println(e.getMessage());
 throw e;
 }
 }
}

清單12模擬了一個實驗性火箭發動機的測試,以檢查發動機的壓力或溫度是否超過了安全閾值。它通過monitor()助手方法執行這個測試。

monitor()方法的try塊在檢測到極端壓力時拋出PressureException,在檢測到極端溫度時拋出TemperatureException。(由于這只是一個模擬,因此使用了隨機數——java.lang.Math類的靜態double random()方法返回一個介于0.0和(接近)1.0之間的隨機數。)try塊后面跟著一個catch塊,這個catch塊的目的是通過輸出警告消息來部分處理異常。然后重新拋出此異常,以便monitor()的調用方法可以完成對異常的處理。

在JDK 7之前,不能在monitor()的throws子句中指定PressureException和TemperatureException,因為catch塊的e參數是java.lang.Exception類型,并且重新拋出異常被視為拋出參數的類型。JDK 7及后續JDK允許在throws子句中指定這些異常類型,因為它們的編譯器可以確定通過throw e拋出的異常來自try塊,并且只能從該塊拋出PressureException和TemperatureException。

因為現在可以指定靜態void monitor()拋出PressureException, TemperatureException,可以在調用monitor()時提供更精確的處理程序,如下面的代碼片段所示:

try
{
 monitor();
}
catch (PressureException pe)
{
 System.out.println("correcting pressure problem");
}
catch (TemperatureException te)
{
 System.out.println("correcting temperature problem");
}

由于JDK 7中的最終重新拋出提供了改進的類型檢查,因此在以前版本的Java下編譯的源代碼可能無法在以后的JDK下編譯。例如,考慮清單13。

清單13.BreakageDemo.java

class SuperException extends Exception
{
}
class SubException1 extends SuperException
{
}
class SubException2 extends SuperException
{
}
public class BreakageDemo
{
 public static void main(String[] args) throws SuperException
 {
 try
 {
 throw new SubException1();
 }
 catch (SuperException se)
 {
 try
 {
 throw se;
 }
 catch (SubException2 se2)
 {
 }
 }
 }
}

清單13可以在JDK 6和更早版本下編譯。但是,它無法在后續版本的 JDK中編譯,因為這些JDK的編譯器會檢測并報告這樣一個事實,即在相應的try語句體中從未拋出subeexception2。這是一個小問題,可能很少會在你的程序中遇到,讓編譯器檢測冗余代碼源是值得的。移除這些冗余代碼可以使代碼更加清晰,并且生成的類文件更小。

StackWalker和StackWalking API

通過Thread或Throwable的getStackTrace()方法獲取堆棧跟蹤代價高昂,并且會影響性能。JVM急切地捕獲整個堆棧的快照(隱藏的堆棧幀除外),即使只需要前幾個幀。此外,其代碼可能必須處理不感興趣的幀,這也很耗時。最后,無法訪問由堆棧幀表示的方法所聲明的類的實際 java.lang.Class 實例。為訪問這個Class對象,必須擴展java.lang.SecurityManager以訪問受保護的getClassContext()方法,該方法返回當前執行堆棧作為 Class 對象的數組。

JDK 9引入了java.lang.StackWalker類(及其嵌套的Option類和StackFrame接口),作為StackTraceElement(加上SecurityManager)的一個性能更高、功能更強的替代方案。

總結

本文完成了對Java異常處理的兩部分介紹。可能需要通過回顧Java教程中的Oracle異常課程來加強對這個的理解。另一個很好的資源是Baeldung的Java異常處理教程,其中包括異常處理中的反模式。

原文標題:Exceptions in Java: Advanced features and types,作者:Jeff Friesen

責任編輯:華軒 來源: 51CTO
相關推薦

2023-12-11 14:19:00

Java程序異常

2018-08-20 16:25:48

編程語言Java異常處理

2017-09-26 11:43:12

Java異常和處理

2023-05-09 15:01:43

JavaScript編程語言異常處理

2010-01-05 09:26:13

.NET 4.0

2020-07-02 22:42:18

Java異常編程

2009-06-25 14:05:40

Java應用技巧

2013-04-07 10:01:26

Java異常處理

2024-12-09 12:00:00

Python編程數據類型轉換

2011-07-05 10:20:38

java

2017-06-02 10:25:26

Java異常處理

2024-09-26 10:51:51

2012-12-21 10:48:20

Java異常

2016-12-15 13:31:20

Java異常處理經驗

2013-04-01 09:39:06

JavaJava異常

2018-07-11 19:41:47

MySQL定義異常異常處理

2009-01-05 09:14:17

.NETcatch性能損失

2015-03-16 16:16:15

JavaJava異常處理Java最佳實踐

2024-02-21 12:18:00

Java虛擬機JVM

2011-04-06 10:52:51

Java異常處理
點贊
收藏

51CTO技術棧公眾號

国产精品久久久久久久久免费高清| 国产乱色精品成人免费视频| 免费成人高清在线视频theav| 91国偷自产一区二区三区成为亚洲经典 | 日韩妆和欧美的一区二区| 中文字幕一区二区人妻痴汉电车| 牛牛国产精品| 国产一区二区三区网站| 日本黄色三级网站| 午夜无码国产理论在线| 亚洲精品水蜜桃| 欧美日韩中文国产一区发布 | 精品一区二区av| 午夜精品久久久久久久久久久久久 | 一区二区三区麻豆| 国产夫妻性爱视频| 日本高清视频一区| 亚洲女子a中天字幕| а天堂中文最新一区二区三区| 免费看黄色的视频| 中文在线观看免费高清| 欧美顶级大胆免费视频| 亚洲精品白浆高清久久久久久| 国产福利视频一区| 精品国产依人香蕉在线精品| 777奇米四色成人影色区| 日韩久久一区二区| 久久久久久久精| 久久激情综合网| 1区2区在线| 国产二区国产一区在线观看| 国产精品福利无圣光在线一区| 18精品爽视频在线观看| 成人影院在线| 亚洲性夜色噜噜噜7777| xxxwww国产| 国产乱码精品一区二区三区亚洲人| 亚洲国产精品尤物yw在线观看| 亚洲精品在线视频观看| 九一国产在线| 久久亚洲综合色一区二区三区 | www.亚洲成人网| 激情在线小视频| 国产精品久久免费看| 日韩欧美一区二区三区四区五区| 天堂√在线中文官网在线| 成人性生交大合| 国产91一区二区三区| 亚洲精品97久久中文字幕| 国产一区在线看| 91免费国产网站| 国产模特av私拍大尺度| 国产自产v一区二区三区c| 成人黄色大片在线免费观看| 国产ts在线观看| 999精品嫩草久久久久久99| 欧美性极品少妇| 国产淫片av片久久久久久| h片在线观看视频免费免费| 亚洲综合色婷婷| 久久久久久久久久久综合| 伊人春色在线观看| 亚洲一区二区欧美日韩| 搞av.com| 悠悠资源网亚洲青| 91国产免费观看| 不卡的av中文字幕| 成人在线视频www| 精品少妇一区二区三区免费观看| 在线免费看黄色片| 神马午夜久久| 日韩在线免费av| 中文字幕av免费在线观看| 欧美精品99| 91av视频在线观看| 中国黄色一级视频| 国产剧情一区二区三区| 成人国产1314www色视频| 无码国产色欲xxxx视频| 日本一区二区免费在线观看视频| 一区二区免费电影| h片在线观看| 色8久久精品久久久久久蜜| 天天干天天av| 国产精品一线| 欧美伦理视频在线观看| 希岛爱理中文字幕| 欧美一区二区麻豆红桃视频| 日韩在线观看免费全| 四虎精品免费视频| 在线观看不卡| 国产精品草莓在线免费观看| 999久久久久久| 97se亚洲国产综合自在线不卡| 欧美中文娱乐网| 国产午夜精品久久久久免费视| 亚洲一区二区三区美女| 91九色丨porny丨国产jk| 欧美xxxx做受欧美护士| 欧美一二三区在线| 97人妻精品一区二区免费| 2023国产精品久久久精品双| 26uuu久久噜噜噜噜| 亚洲一级av毛片| 成人免费视频一区| 伊人久久大香线蕉午夜av| a毛片不卡免费看片| 欧美性色综合网| 日本三级日本三级日本三级极| 成人午夜av| 97高清免费视频| 国产精品怡红院| 久久天天做天天爱综合色| 神马午夜伦理影院| 成人国产一区| 精品一区精品二区| 精品小视频在线观看| 理论电影国产精品| 免费一区二区三区| 国产精品一品| 这里是久久伊人| 久久久久久久久福利| 国产色综合网| 国产精品三区在线| gogo在线高清视频| 欧美男男青年gay1069videost| 丰满少妇一区二区| 亚洲欧洲一区| 不卡一区二区三区视频| 欧美性天天影视| 欧美系列亚洲系列| 国产熟妇久久777777| 国产欧美午夜| 国内精品一区二区| 草草影院在线| 亚洲第一中文字幕| 日本三级中文字幕| 高清成人免费视频| 伊人久久大香线蕉午夜av| 国产国产一区| 色七七影院综合| 97av视频在线| 性欧美13一14内谢| 欧美专区在线| 久久婷婷人人澡人人喊人人爽| free性欧美hd另类精品| 91精品国模一区二区三区| 亚洲熟女少妇一区二区| 久久精品国产第一区二区三区| 亚洲日本japanese丝袜| 欧美三级一级片| 国产综合色区在线观看| 亚洲欧洲高清在线| 亚洲综合久久网| 国产拍揄自揄精品视频麻豆| 香蕉视频禁止18| 色狼人综合干| 国产高清在线不卡| 午夜在线小视频| 777午夜精品免费视频| 日韩一级片av| 成人黄色一级视频| 人妻熟女一二三区夜夜爱| 国产日产精品_国产精品毛片| 国产成人综合一区二区三区| porn亚洲| 日韩三级精品电影久久久| 久久这里只有精品免费| 91农村精品一区二区在线| 91淫黄看大片| 欧美在线高清| 六月婷婷久久| 成人福利片在线| 欧美成在线观看| 亚洲欧美丝袜中文综合| 欧美亚日韩国产aⅴ精品中极品| 老司机精品免费视频| av日韩国产| 欧美xxxx在线观看| 一级免费在线观看| 国产欧美视频一区二区| 中文字幕在线视频精品| 影音先锋日韩资源| 青青影院一区二区三区四区| 亚洲人成网站在线在线观看| 欧美黄色成人网| 国内精品在线视频| 欧美一区二区精美| 久久不卡免费视频| 国产精品福利在线播放| 国产伦精品一区三区精东| 日韩影院精彩在线| wwwjizzjizzcom| 精品成人影院| 国产亚洲精品美女久久久m| 国产成人免费9x9x人网站视频 | 屁屁影院国产第一页| 日本不卡的三区四区五区| 成年人深夜视频| 精品久久国产| 国产一级精品aaaaa看| 国产福利一区二区三区在线播放| 久久久免费电影| 在线毛片网站| 九色|91porny| 路边理发店露脸熟妇泻火| 日韩精品导航| 96国产粉嫩美女| 成人看片网站| 韩国欧美亚洲国产| 麻豆视频在线播放| 亚洲欧美综合图区| 欧美 日韩 综合| 欧美一区二区视频在线观看2022 | 国产精品区一区二区三在线播放| 粉嫩av一区二区三区四区五区| 性视频1819p久久| 日韩精品卡一| 久久精品国产一区二区三区| 国产乱视频在线观看| 亚洲高清一区二| 亚洲第一色视频| 911精品产国品一二三产区| 国产一卡二卡三卡| 色哟哟欧美精品| 日韩久久久久久久久| 一区二区三区在线观看动漫| 久久久久人妻一区精品色| 国产女人aaa级久久久级| 中文精品在线观看| 91免费视频大全| 51国偷自产一区二区三区 | 亚洲一区中文日韩| 性欧美videos| 亚洲欧美日韩电影| 小向美奈子av| 亚洲天堂中文字幕| 国产精品丝袜一区二区| 中文字幕一区二区视频| 女人18毛片毛片毛片毛片区二| 欧美国产一区二区| 人人艹在线视频| 国产精品理论片在线观看| 国产精品夜夜夜爽阿娇| 一色屋精品亚洲香蕉网站| 国产色无码精品视频国产| 亚洲色图另类专区| 欧美日韩大片在线观看| 亚洲一区中文在线| 日韩黄色精品视频| 欧美日韩中文在线观看| 人人爽人人爽人人片av| 欧美在线三级电影| 中文字幕在线观看第二页| 欧美日本一道本| 国产日韩免费视频| 精品播放一区二区| 蜜桃视频在线免费| 中文字幕精品一区久久久久| 日韩在线观看www| 久久国产精品影片| 美女网站久久| 国产精品yjizz| 9l视频自拍蝌蚪9l视频成人| 国产91亚洲精品一区二区三区| 99ri日韩精品视频| 国产伦理久久久| 久久综合亚洲| 亚洲一区影院| 欧美日韩亚洲三区| 无码aⅴ精品一区二区三区浪潮 | 亚洲国产精品自拍| 黄色在线视频网址| 欧美裸体一区二区三区| 亚洲成熟女性毛茸茸| 亚洲欧美福利视频| 麻豆网站在线| 性色av一区二区三区| 成人在线爆射| 99三级在线| 国产伦精品一区二区三区视频| 一区二区av| 国产一区二区三区的电影| 国产小视频精品| 成人综合婷婷国产精品久久 | 亚洲自拍偷拍欧美| 日本免费在线观看视频| 日韩视频永久免费| 国产福利小视频在线观看| 欧美巨乳在线观看| 欧洲精品一区二区三区| 91在线免费看片| 国产欧美日韩影院| 成人午夜精品久久久久久久蜜臀| 日本在线观看不卡视频| 超碰caoprom| 亚洲欧美在线观看| 亚洲黄网在线观看| 欧美成人性福生活免费看| www.黄在线观看| 欧美在线一区二区三区四| 精品国产三区在线| 无遮挡亚洲一区| 国产精品呻吟| 最新中文字幕日本| 国产精品成人在线观看| 日韩精品一区二区亚洲av| 欧美成人三级在线| 精品美女在线观看视频在线观看| 国产精品96久久久久久| 美女一区二区在线观看| 国产 国语对白 露脸| 日本成人超碰在线观看| 醉酒壮男gay强迫野外xx| 一区二区三区在线视频观看58| 中文字幕 视频一区| 亚洲毛片在线免费观看| 97超碰在线免费| av一区二区三区四区电影| 国产精品99久久精品| 一区二区三区国产免费| 2017欧美狠狠色| 欧美不卡视频在线观看| 精品国产乱码久久久久久影片| 国产鲁鲁视频在线观看特色| 国产精品电影网| 亚洲人成精品久久久| 尤物av无码色av无码| 国产69精品久久99不卡| 朝桐光av在线| 日韩一区二区免费视频| 国产盗摄在线观看| 亚洲va欧美va在线观看| 国产精品99久久精品| 日本不卡一区二区在线观看| 中文字幕中文字幕一区| 在线观看免费视频一区| 色婷婷成人综合| 久久精品 人人爱| 亚洲午夜在线观看| 麻豆精品一区二区综合av| 国产aaaaaaaaa| 欧美三级日韩三级| 日本蜜桃在线观看| 国产欧美 在线欧美| 天天射天天综合网| 免费国偷自产拍精品视频| 亚洲乱码日产精品bd| 亚洲精品18p| 欧美性做爰毛片| 国产一区二区三区四区五区| 久久久精品麻豆| 中文字幕在线观看不卡| 99久久精品国产一区色| 久久久久中文字幕| 日韩中文av| 亚洲娇小娇小娇小| 亚洲欧美日韩国产手机在线| 黑人乱码一区二区三区av| 97**国产露脸精品国产| 国产一区二区精品久| 亚洲一区精品视频在线观看| 亚洲欧美日韩国产一区二区三区| 人妻少妇一区二区三区| 奇米一区二区三区四区久久| 成久久久网站| 国模大尺度视频| 欧美小视频在线| av大片在线观看| 不卡一卡2卡3卡4卡精品在| 久久精品女人天堂| 中文字幕求饶的少妇| 亚洲成人av在线| 日本美女久久| 中国丰满熟妇xxxx性| 国产亚洲精品bt天堂精选| 国产精品视频第一页| 69精品小视频| 久久国产精品亚洲人一区二区三区| 日本一二三区在线| 精品日韩视频在线观看| 午夜在线小视频| 久久精品女人的天堂av| 久久99久久久久久久久久久| 国产中文字字幕乱码无限| 在线不卡国产精品| 99久久人爽人人添人人澡| 国产精品无码一本二本三本色| 亚洲久本草在线中文字幕| 天堂资源中文在线| 成人性生交大片免费看小说 | 亚洲一区二区在线视频| 久久久久久女乱国产| 5566av亚洲| 麻豆久久一区二区| 日韩乱码一区二区| 欧美裸体男粗大视频在线观看| 国产成人影院| 国产精品久久久久久亚洲色| 欧美日韩高清一区|