在JDK 7中,引入了try-with-resource
用於替代在try-catch-finally
中手動的資源關閉
通常需要我們手動釋放的資源包括:
- 文件/流資源
- socket資源
- 數據庫連接資源
這些資源不能自動的被回收,長時間無效佔用,當超過最大限制後,將會無資源可用,最終導致系統無法正常運行
以文件拷貝爲例演示JDK 7之前和引入try-with-resource
後的差異
- 使用
try-catch-finally
實現文件拷貝的資源管理
輸入輸出流需要在try塊外先定義,並在finally中按初始化順序的倒序逐一進行釋放
@Test
public void copyFile() {
//路徑
String originalUrl = "lib/FileCopy.java";
String outUrl = "outTest/out.txt";
//資源對象
FileInputStream fileInputStream = null;
FileOutputStream fileOutputStream = null;
try {
//初始化資源對象
fileInputStream = new FileInputStream(originalUrl);
fileOutputStream = new FileOutputStream(outUrl);
int content;
//讀取並寫入
while ((content = fileInputStream.read()) != -1) {
fileOutputStream.write(content);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {//資源關閉
if (fileOutputStream != null) {
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fileInputStream!= null) {
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
使用try-with-resource
實現文件拷貝的資源管理
try-with-resource
的結構是:
try (
//資源定義
) {
//邏輯代碼
} catch(Exception e) {
//異常處理
}
與try-catch-finally
相比,在try後多了一對小括號用於資源定義,且不需要再手動的關閉資源了
具體代碼如下:
/**
* 基於JDK7及以後的
* try - with - resource
*/
@Test
public void newCopyFile() {
//路徑
String originalUrl = "lib/FileCopy.java";
String outUrl = "outTest/out.txt";
/**
* 小括號內定義資源,不需要顯示的關閉資源,會自動的關閉
* try - with - resource
*/
try (
//定義資源
FileInputStream fileInputStream = new FileInputStream(originalUrl);
FileOutputStream fileOutputStream = new FileOutputStream(outUrl);
) {
int content;
while ((content = fileInputStream.read()) != -1) {
fileOutputStream.write(content);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
從上面兩段代碼可以看出,在try-with-resource
中的小括號內定義資源就不用再手動的進行釋放了
爲了更清楚的弄明白是怎樣實現了,查看下反編譯的代碼
這裏反編譯的代碼來自IDEA編譯後的target文件下自動反編譯的文件。
使用try-with-resource
的反編譯代碼如下
@Test
public void newCopyFile() {
String originalUrl = "lib/FileCopy.java";
String outUrl = "outTest/out.txt";
try {
FileInputStream fileInputStream = new FileInputStream(originalUrl);
Throwable var4 = null;
try {
FileOutputStream fileOutputStream = new FileOutputStream(outUrl);
Throwable var6 = null;
try {
int content;
try {
while((content = fileInputStream.read()) != -1) {
fileOutputStream.write(content);
}
} catch (Throwable var33) {
var6 = var33;
throw var33;
}
} finally {
if (fileOutputStream != null) {
if (var6 != null) {
try {
fileOutputStream.close();
} catch (Throwable var32) {
var6.addSuppressed(var32);
}
} else {
fileOutputStream.close();
}
}
}
} catch (Throwable var35) {
var4 = var35;
throw var35;
} finally {
if (fileInputStream != null) {
if (var4 != null) {
try {
fileInputStream.close();
} catch (Throwable var31) {
var4.addSuppressed(var31);
}
} else {
fileInputStream.close();
}
}
}
} catch (FileNotFoundException var37) {
var37.printStackTrace();
} catch (IOException var38) {
var38.printStackTrace();
}
}
可以很容易的發現,編譯器將try-with-resource
格式的代碼編譯成了嵌套的try-catch-finally
的形式,所以可以證明try-with-resource
能實現自動添加資源釋放
使用注意點:
- 資源需要實現AutoCloseable接口
- 資源對象被return的情況下,由調用方關閉
- ByteArrayInputStream等不需要檢查關閉資源對象
- 使用socket獲取的InputStream和OutputStream對象不需要關閉
(如果直接關閉會關閉socket整個連接,需要調用socket的方法進行關閉-shutdowInput 和 shutdownOutput)