Java 级联I/O流是一种糟糕的做法吗?
以下代码是否导致资源泄漏Java 级联I/O流是一种糟糕的做法吗?,java,Java,以下代码是否导致资源泄漏 JarOutputStream jo = null; try { File f = new File("myinput.txt"); jo = new JarOutputStream(new FileOutputStream(f)); } catch(IOException ex){ } finally { try { if(jo!=null)
JarOutputStream jo = null;
try
{
File f = new File("myinput.txt");
jo = new JarOutputStream(new FileOutputStream(f));
}
catch(IOException ex){
}
finally
{
try {
if(jo!=null)
jo.close();
} catch (IOException e) {
}
}
如果创建
JarOutputStream
失败,这是否会导致资源泄漏,因为FileOutputStream
已经生成?是的,您可能会发生泄漏,因为调用JarOutputStream的构造函数时,流对象链接到您的文件
JarOutputStream jo = null;
FileOutputStream myOut = null;
try
{
myOut = new FileOutputStream(new File("myinput.txt"));
jo = new JarOutputStream(myOut);
}
catch(IOException ex){
}
finally
{
try {
if(jo!=null)
jo.close();
if(myOut!=null)
myOut.close();
} catch (IOException e) {
}
}
假设是的,如果
JarOutputStream
构造函数抛出异常,它将泄漏资源。(当然,javadoc在这一点上并不清楚。)
然而,对于这种特殊用法,很难看到构造函数如何抛出异常。对源代码(Java6到Java8)的检查表明,这里唯一可能发生的异常是Error
的子类。。。您不应该尝试从中恢复
那么这是坏习惯吗 这真的取决于上下文
- 如果应用程序试图从
中的异常中恢复,并重复执行相同的操作,那么您可能会因为资源泄漏而陷入严重问题JarOutputStream
- 如果不是,那么。。。没什么大不了的
另一个问题是,编写代码的最佳方式是什么。我倾向于这样写:
File f = new File("myinput.txt");
try (FileOutputStream fo = new FileOutputStream(f);
JarOutputStream jo = new JarOutputStream(fo)) {
// write stuff to jo
} catch(IOException ex) {
// diagnose errors
}
但是,上面的代码将
JarOutputStream
限制为try块。如果您确实需要在块之外使用流对象(例如,因为您正在返回它),那么尝试使用资源没有帮助。您需要对其进行编码并明确关闭文件输出流。是的,这肯定是一种不好的做法,因为它会导致泄漏
一般来说,每个可关闭的资源都应该打开到try-with-resources语句中。此外,它使代码更具可读性:
try (OutputStream out=new FileOutputStream(file))
{
try (JarOutputStream jo=new JarOutputStream(out))
{
...
}
// optional catches...
}
// optional catches...
另外,请记住,catch
子句不应为空(它可能隐藏执行错误)。您回答了自己的问题:
jo = new JarOutputStream(new FileOutputStream(f));
相等于
fos = new FileOutputStream(f);
jo = new JarOutputStream(fos); //if this throws exception, fos is never closed
要解决此问题,您可以:
将一个try块包装成另一个,这可能非常难看。如果您坚持使用Java版本,而没有下一个想法,我建议您不要将代码重写为“完全”安全的,除非真的有必要(您一直在打开大量资源,并且您的程序运行时间太长,如果它们没有关闭,这将非常重要)
或者您可以使用自Java 7开始提供的:
File f = new File ("foo");
try(FileOutputStream fos = new FileOutputStream (f);
JarOutputStream jos = new JarOutputStream (fos)) {
//do sth
} catch (IOException e) {
//log exception, etc.
}
这将使两个流在代码执行后自动关闭,此外,这比手工编写的代码要好得多