Java如何實現(xiàn)自動關(guān)閉資源?
1. 為什么要手動關(guān)閉Java資源對象?
首先:解釋Java的資源對象,它主要包括IO對象,數(shù)據(jù)庫連接對象。比如常見的InputStream、OutputStream、Reader、Writer、Connection、Statement、ResultSet、Socket等等,先代碼列舉一個示例:
FileInputStream f = new FileInputStream("sample.txt");
f.close();//f對象即需要手動關(guān)閉的資源對象
上述代碼中f對象即需要手動關(guān)閉的資源對象。
其次:如果類似的資源對象沒有及時的手動關(guān)閉,這個對象就會一直占據(jù)內(nèi)存,當這樣的對象越來越多,那內(nèi)存被占用的就會越來越多,久而久之就可能造成OutOfMemory,俗稱內(nèi)存溢出。
這時應(yīng)該有人會問,Java不是有自己的垃圾回收機制GC么?不是可以自動回收么?
這個問題問的好,我也一度非常困惑。
第一:首先我們先了解一下GC的原理:
在Java中,當沒有對象引用指向原先分配給某個對象的內(nèi)存時,該內(nèi)存便成為垃圾。JVM的一個系統(tǒng)級線程會自動釋放該內(nèi)存塊。垃圾回收意味著程序不再需要的對象是"無用信息",這些信息將被丟棄。當一個對象不再被引用的時候,內(nèi)存回收它占領(lǐng)的空間,以便空間被后來的新對象使用。
首先:GC只能回收內(nèi)存。至于各種stream之類,他們下邊一般還開啟了各種其他的系統(tǒng)資源,比如文件,比如輸入輸出設(shè)備(鍵盤/屏幕等),等等。而這些設(shè)備第一是不能自動關(guān)閉(因為誰知道你程序要用它到什么時候啊),另一個系統(tǒng)內(nèi)數(shù)量有限(比如鍵盤/屏幕同一時間只有一個)。最后,文件和數(shù)據(jù)庫連接之類的東西還存在讀寫鎖定的問題。這些都導致用戶必須手動處理這些資源的開啟和關(guān)閉。
其次為了“避免”程序員忘了自己釋放那些資源,Java提供了finalizer、PhantomReference之類的機制來讓程序員向GC注冊“自動回調(diào)釋放資源”的功能。但GC回調(diào)它們的時機不確定,所以只應(yīng)該作為最后手段來使用,主要手段還是自己關(guān)閉最好。
PS:關(guān)于GC其實有很多的知識可以深度挖掘,比如各種回收算法,finalize()方法等等,大家感興趣的話可以自行搜索研究,我就不班門弄斧了。
2. 怎樣正確的手動關(guān)閉Java資源對象?
先說一種最常見的關(guān)閉方式,在finally中進行關(guān)閉:
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中進行資源對象關(guān)閉屬于Best Practice。因為即使對象f在使用的過程中出現(xiàn)異常,也能保證程序不會跳過后續(xù)的關(guān)閉操作。
特別注意,自從Java1.7開始,支持了try-with-resources寫法,即將資源對象聲明的過程放在try()的括號里面,這樣java在資源對象使用完成之后會自動關(guān)閉。
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
}
另外還有一些第三方庫提供了一些統(tǒng)一的關(guān)閉處理方法,例如
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)的形式關(guān)閉資源對象,實際內(nèi)部實現(xiàn)依然是調(diào)用.close()方法。內(nèi)部實現(xiàn)代碼如下:
public static void closeQuietly(final Closeable closeable) {
try {
if (closeable != null) {
closeable.close();
}
} catch (final IOException ioe) {
// ignore
}
}
以上就是手動關(guān)閉Java資源對象的幾種推薦寫法,希望對你有所幫助。