-
1 # 技術呆東
-
2 # 瑁子
1. 為什麼要手動關閉Java資源物件?
首先:解釋Java的資源物件,它主要包括IO物件,資料庫連線物件。比如常見的InputStream、OutputStream、Reader、Writer、Connection、Statement、ResultSet、Socket等等,先程式碼列舉一個示例:
FileInputStream f = new FileInputStream("sample.txt");
f.close();//f物件即需要手動關閉的資源物件
上述程式碼中f物件即需要手動關閉的資源物件。
其次:如果類似的資源物件沒有及時的手動關閉,這個物件就會一直佔據記憶體,當這樣的物件越來越多,那記憶體被佔用的就會越來越多,久而久之就可能造成OutOfMemory,俗稱記憶體溢位。
這時應該有人會問,Java不是有自己的垃圾回收機制GC麼?不是可以自動回收麼?
這個問題問的好,我也一度非常困惑。
第一:首先我們先了解一下GC的原理:
在Java中,當沒有物件引用指向原先分配給某個物件的記憶體時,該記憶體便成為垃圾。JVM的一個系統級執行緒會自動釋放該記憶體塊。垃圾回收意味著程式不再需要的物件是"無用資訊",這些資訊將被丟棄。當一個物件不再被引用的時候,記憶體回收它佔領的空間,以便空間被後來的新物件使用。
首先:GC只能回收記憶體。至於各種stream之類,他們下邊一般還開啟了各種其他的系統資源,比如檔案,比如輸入輸出裝置(鍵盤/螢幕等),等等。而這些裝置第一是不能自動關閉(因為誰知道你程式要用它到什麼時候啊),另一個系統內數量有限(比如鍵盤/螢幕同一時間只有一個)。最後,檔案和資料庫連線之類的東西還存在讀寫鎖定的問題。這些都導致使用者必須手動處理這些資源的開啟和關閉。
其次為了“避免”程式設計師忘了自己釋放那些資源,Java提供了finalizer、PhantomReference之類的機制來讓程式設計師向GC註冊“自動回撥釋放資源”的功能。但GC回撥它們的時機不確定,所以只應該作為最後手段來使用,主要手段還是自己關閉最好。
PS:關於GC其實有很多的知識可以深度挖掘,比如各種回收演算法,finalize()方法等等,大家感興趣的話可以自行搜尋研究,我就不班門弄斧了。
2. 怎樣正確的手動關閉Java資源物件?
先說一種最常見的關閉方式,在finally中進行關閉:
FileInputStream f;
try{
f= new FileInputStream("sample.txt");
//something that uses f and sometimes throws an exception
}
catch(IOException ex){
/* Handle it somehow */
}
finally{
f.close();
}
這裡在finally中進行資源物件關閉屬於Best Practice。因為即使物件f在使用的過程中出現異常,也能保證程式不會跳過後續的關閉操作。
特別注意,自從Java1.7開始,支援了try-with-resources寫法,即將資源物件宣告的過程放在try()的括號裡面,這樣java在資源物件使用完成之後會自動關閉。
try (
FileOutputStream fileOutputStream = new FileOutputStream("E:\\A.txt");
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
DataOutputStream out = new DataOutputStream(bufferedOutputStream)
)
{
out.write(data1);
} catch (Exception e) {
// TODO: handle exception
}
另外還有一些第三方庫提供了一些統一的關閉處理方法,例如
import org.apache.commons.io.IOUtils;
public static void main(String[] args) throws Exception{
FileOutputStream fileOutputStream = null;
BufferedOutputStream bufferedOutputStream=null;
DataOutputStream out=null;
byte[] data1 = "這個例子測試檔案寫".getBytes("GB2312");
try {
fileOutputStream = new FileOutputStream("E:\\A.txt");
bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
out = new DataOutputStream(bufferedOutputStream);
out.write(data1);
} catch (Exception e) {
// TODO: handle exception
} finally {
IOUtils.closeQuietly(out);
}
}
這個apache提供的IOUtils類庫可以透過IOUtils.closeQuietly(e)的形式關閉資源物件,實際內部實現依然是呼叫.close()方法。內部實現程式碼如下:
public static void closeQuietly(final Closeable closeable) {
try {
if (closeable != null) {
closeable.close();
}
} catch (final IOException ioe) {
// ignore
}
}
-
3 # 創域百家
Java 7新增機制——自動關閉資源的try語句
1 自動關閉資源介紹
Java 7增強了try語句的功能——它允許在try關鍵字後跟一對圓括號,圓括號可以宣告,初始化一個或多個資源,此處的資源指得是那些必須在程式結束時必須關閉的資源(比如資料庫連線,網路連線等),try語句在該語句結束時自動關閉這些資源。
為了保證try語句可以正常關閉資源,這些資源實現類必須實現Closeable或AutoCloseable介面,實現這些類就必須實現close方法。
2 程式碼示例
import java.io.*;
public class AutoCloseTest
{
public static void main(String[] args)
throws IOException
{
try (
// 宣告、初始化兩個可關閉的資源
// try語句會自動關閉這兩個資源。
BufferedReader br = new BufferedReader(
new FileReader("AutoCloseTest.java"));
PrintStream ps = new PrintStream(new
FileOutputStream("a.txt")))
{
// 使用兩個資源
System.out.println(br.readLine());
ps.println("莊生曉夢迷蝴蝶");
}
}
}
3 執行結果
E:\test\Java\Java8\ExceptionTEST\src>java AutoCloseTest
import java.io.*;
檔案a.txt中生成
莊生曉夢迷蝴蝶
4 結果分析
try後的小括號分別宣告,初始化兩個IO流,由於BufferedReader,PrintStream都實現了Closeable介面,而且它們放在try語句中宣告和初始化,所有try語法會自動關閉它們。因此上面的程式碼是安全的。
回覆列表
以io流處理為例,當我們在進行io流處理資料的時候,在最後需要把我們的流資源釋放掉,一般都是在finally語句後面,進行手動關閉,來保證資源關閉。但是在java7之後新加一個功能,我們只要把需要進行關閉資源的語句放在try()裡面,可以省去我們進行手動關閉,程式碼看上去也會更加簡潔