儘量避免諸如x.read(new FileInputStream(sourceFile)); 的寫法
一不小心,順手寫了x.read(new FileInputStream(sourceFile)); 這樣的程式碼,卻引得自己花費了半個多小時去除錯問題。
原因是這樣的:在開發某一個feature的時候,需要對操作的檔案進行backup,於是寫了諸如下面的程式碼(以下僅是演示程式碼,與實際要簡易,僅供參考):
* XXX是一個處理類,soureFile是一個輸入的File object
*/
XXX x =new XXX();
x.read(new FileInputStream(sourceFile));
..... //complex logic process
String sourceFilePath
int maxBackups =3;
List<File> files =new ArrayList<File>();
files.add(traFile);
String base = sourceFile.getPath() +'.';
for (int generations =1; true; generations++) {
File f =new File(base + generations);
files.add(f);
if (!f.exists()) {
break;
}
}
int
for (int generation = generations -1; generation >0; generation--) {
if (generation > maxBackups) {
files.get(generation).delete();
} else {
if(files.get(generation).exists()) files.get(generation).delete();
files.get(generation -1).renameTo(files.get(generation));
}
}
..... //complex logic processx.write(new FileOutputStream(sourceFilePath));
當寫完以後,執行TestCase並不是每次都成功,有時候就可以正確生成backup檔案,而有時候則沒有生成(沒有生成的概率大很多)。跟蹤了一下,發現當沒有正確生成backup的時候,是那段file renameTo沒有執行成功。就是如上面程式碼中“粗粉紅色的標記的”:files.get(generation - 1).renameTo(files.get(generation))
然後進行Debug狀態的step by step執行,卻每次都成功了。
仔細了想了想,可能的原因是 files.get(generation - 1) 這個所引物件的物件,在執行renameTo操作的時候,可能還在被某個資源鎖定,而沒有釋放。
帶著個這個想法,把程式碼從頭再過了一遍,終於發現是 x.read(new FileInputStream(sourceFile)); 這段出了問題。因為XXX這個類,在內部處理過程中,並沒有對輸入的inputstream進行關閉(當然,這是一個正確的設計和實現,一般我們在開發過程中,都不會在“使用者”內部關閉外部的流,這樣危險性非常大)。
但是,為了程式碼了簡潔,很隨意的就進行了 x.read(new FileInputStream(sourceFile)); 這樣的書寫。
正是這樣的書寫,讓後面針對soureFile的操作不能執行成功。因為所用在這個soureFile上的InputStream流並沒有被“真正關閉”。
於是改成:
InputStream is = new FileInputStream(sourceFile);
x.read(is);
is.close;
就可以了。
當然,並不是說諸如 x.read(new FileInputStream(sourceFile)); 的寫法不能用,而是儘量避免在“在同一個方法開閉區間內多次引用sourceFile”,在這樣場合下,就肯定不能用了。