1.利用Try-Catch-Finally管理資源(舊的代碼風格)
在java7以前,程式中使用的資源需要被明确地關閉,這個體驗有點繁瑣。
下面的方法讀取檔案,然後用System.out列印:
private static void printFile() throws IOException {
InputStream input = null;
try {
input = new FileInputStream("F:\\BaiduNetdiskDownload\\1.text");
int data = input.read();
while(data != -1){
System.out.print((char) data);
data = input.read();
}
} finally {
if(input != null){
input.close();
}
}
}
上面代碼中黑體字的程式可能會抛出異常。正如你所看到的,try語句塊中有3個地方能抛出異常,finally語句塊中有一個地方會能出異常。
不論try語句塊中是否有異常抛出,finally語句塊始終會被執行。這意味着,不論try語句塊中發生什麼,InputStream 都會被關閉,或者說都會試圖被關閉。如果關閉失敗,InputStream’s close()方法也可能會抛出異常。
假設try語句塊抛出一個異常,然後finally語句塊被執行。同樣假設finally語句塊也抛出了一個異常。那麼哪個異常會根據調用棧往外傳播?
即使try語句塊中抛出的異常與異常傳播更相關,最終還是finally語句塊中抛出的異常會根據調用棧向外傳播。
在java7中,對于上面的例子可以用
try-with-resources
結構這樣寫:
private static void printFileJava7() throws IOException {
try(FileInputStream input = new FileInputStream("F:\\BaiduNetdiskDownload\\1.text")) {
int data = input.read();
while(data != -1){
System.out.print((char) data);
data = input.read();
}
}
}
注意方法中的第一行:
try(FileInputStream input = new FileInputStream("file.txt")) {
這就是
try-with-resources
結構的用法。FileInputStream 類型變量就在try關鍵字後面的括号中聲明。而且一個FileInputStream 類型被執行個體化并被賦給了這個變量。
當try語句塊運作結束時,FileInputStream 會被自動關閉。這是因為FileInputStream 實作了java中的java.lang.AutoCloseable接口。所有實作了這個接口的類都可以在
try-with-resources
結構中使用。
當
try-with-resources
結構中抛出一個異常,同時FileInputStreami被關閉時(調用了其close方法)也抛出一個異常,
try-with-resources
結構中抛出的異常會向外傳播,而FileInputStreami被關閉時抛出的異常被抑制了。這與文章開始處利用舊風格代碼的例子(在finally語句塊中關閉資源)相反。
2.使用多個資源
你可以在塊中使用多個資源而且這些資源都能被自動地關閉。下面是例子:
private static void printFileJava7ForMultipart() throws IOException {
try (FileInputStream input = new FileInputStream("file.txt");
BufferedInputStream bufferedInput = new BufferedInputStream(input)) {
int data = bufferedInput.read();
while (data != -1) {
System.out.print((char) data);
data = bufferedInput.read();
}
}
}
上面的例子在try關鍵字後的括号裡建立了兩個資源——
FileInputStream
和
BufferedInputStream
。當程式運作離開try語句塊時,這兩個資源都會被自動關閉。
這些資源将按照他們被建立順序的逆序來關閉。首先BufferedInputStream 會被關閉,然後FileInputStream會被關閉。
3.自定義AutoClosable 實作
這個
try-with-resources
結構裡不僅能夠操作java内置的類。你也可以在自己的類中實作java.lang.AutoCloseable接口,然後在
try-with-resources
結構裡使用這個類。
AutoClosable 接口僅僅有一個方法,接口定義如下:
public interface AutoClosable {
public void close() throws Exception;
}
任何實作了這個接口的方法都可以在
try-with-resources
結構中使用。下面是一個簡單的例子:
public class MyAutoClosable implements AutoCloseable {
public void doIt() {
System.out.println("MyAutoClosable doing it!");
}
@Override
public void close() throws Exception {
System.out.println("MyAutoClosable closed!");
}
}
doIt()是方法不是AutoClosable 接口中的一部分,之是以實作這個方法是因為我們想要這個類除了關閉方法外還能做點其他事。
下面是MyAutoClosable 在
try-with-resources
結構中使用的例子:
private static void myAutoClosable() throws Exception {
try(MyAutoClosable myAutoClosable = new MyAutoClosable()){
myAutoClosable.doIt();
}
}
當方法
myAutoClosable.doIt()
被調用時,下面是列印到System.out的輸出:
MyAutoClosable doing it!
MyAutoClosable closed!
通過上面這些你可以看到,不論try-catch中使用的資源是自己創造的還是java内置的類型,
try-with-resources
都是一個能夠確定資源能被正确地關閉的強大方法。