想象這麼一個情景,我們需要使用一個資源,在使用完之後需要關閉該資源,並且使用該資源的過程中有可能有異常丟擲。此時我們都會想到用try-catch語句,在finally中關閉該資源。此時會有一個問題,如果關閉該資源的時候也丟擲了異常呢?見如下例子:

package com.rundong;

import java.io.IOException;

public class ResourceTest implements AutoCloseable{
@Override
public void close() throws IOException {
throw new IOException("close Exception");
}
}
package com.rundong;

import java.io.IOException;
import java.sql.SQLException; public class Main {
public static void main(String[] args) {
try {
throwTest();
} catch (SQLException e) {
System.out.println("catch SQLException");
e.printStackTrace();
} catch (IOException e) {
System.out.println("catch IOException");
e.printStackTrace();
}
} static void throwTest() throws SQLException, IOException {
ResourceTest resource = null;
try {
resource = new ResourceTest();
throw new SQLException("the first Exception");
} catch (SQLException e) {
System.out.println("catch the first Exception");
// do something
throw e;
} finally {
if (resource != null) resource.close();
}
}
}

輸出結果:

發現什麼問題了嗎?

finally中丟擲的異常將try中丟擲的異常覆蓋了!我們無法捕獲到第一個異常!

Java 7 的解決方式:

package com.rundong;

import java.io.IOException;
import java.sql.SQLException; public class Main {
public static void main(String[] args) {
try {
throwTest();
} catch (SQLException e) {
System.out.println("catch SQLException");
e.printStackTrace();
} catch (IOException e) {
System.out.println("catch IOException");
e.printStackTrace();
}
} static void throwTest() throws SQLException, IOException {
try (ResourceTest resource = new ResourceTest()) {
throw new SQLException("the first Exception");
} catch (SQLException e) {
System.out.println("catch the first Exception");
// do something
throw e;
}
}
}

輸出結果:

try-with-resources的寫法就能很好的處理這樣的情景,它會自動關閉資源,並且如果關閉資源時也丟擲了異常,不會覆蓋原有異常的丟擲。

使用條件:實現了AutoCloseable介面的類。

同時,這也提示著我們,永遠不要嘗試在finally中丟擲異常!