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.
}
这将使两个流在代码执行后自动关闭,此外,这比手工编写的代码要好得多