Java中的流关闭模式变体

Java中的流关闭模式变体,java,design-patterns,resource-management,Java,Design Patterns,Resource Management,在处理流时,我经常在Java文档和其他人的代码中看到以下模式: FileInputStream fis = null; try { fis = new FileInputStream("some.file"); // do something useful with fis } finally { if (fis != null) { fis.close(); } } 但我个人更喜欢另一种模式: FileInputStream fis = new FileInputSt

在处理流时,我经常在Java文档和其他人的代码中看到以下模式:

FileInputStream fis = null;
try {
  fis = new FileInputStream("some.file");
  // do something useful with fis
} finally {
  if (fis != null) {
    fis.close();
  }
}
但我个人更喜欢另一种模式:

FileInputStream fis = new FileInputStream("some.file");
try {
  // do something useful with fis
} finally {
  fis.close();
}
我喜欢后者的简洁,我认为它是正确的。但我说的对吗?有没有客观的理由选择一个而不是另一个

更新 即使我写的例子和我在现实生活中如何写这样的代码差不多,我的模式仍然更简洁。比较文件编制方法:

public Object processFile(String fn) throws MyException {
  FileInputStream fis = null;
  try {
    fis = new FileInputStream(fn);
    return new Object(); // somehow derived from fis
  } catch (FileNotFoundException e) {
    throw new MySubExceptionForNotFound(e);
  } catch (IOException e) {
    throw new MySubExceptionForIoError(e);
  } finally {
    if (fis != null) {
      IOUtils.closeQuietly(fis);
    }
  }
}
public Object processFile(String fn) throws MyException {
  try {
    FileInputStream fis = new FileInputStream(fn);
    try {
      return new Object(); // somehow derived from fis
    } finally {
      IOUtils.closeQuietly(fis);
    }
  } catch (FileNotFoundException e) {
    throw new MySubExceptionForNotFound(e);
  } catch (IOException e) {
    throw new MySubExceptionForIoError(e);
  }
}
按照我的方法:

public Object processFile(String fn) throws MyException {
  FileInputStream fis = null;
  try {
    fis = new FileInputStream(fn);
    return new Object(); // somehow derived from fis
  } catch (FileNotFoundException e) {
    throw new MySubExceptionForNotFound(e);
  } catch (IOException e) {
    throw new MySubExceptionForIoError(e);
  } finally {
    if (fis != null) {
      IOUtils.closeQuietly(fis);
    }
  }
}
public Object processFile(String fn) throws MyException {
  try {
    FileInputStream fis = new FileInputStream(fn);
    try {
      return new Object(); // somehow derived from fis
    } finally {
      IOUtils.closeQuietly(fis);
    }
  } catch (FileNotFoundException e) {
    throw new MySubExceptionForNotFound(e);
  } catch (IOException e) {
    throw new MySubExceptionForIoError(e);
  }
}
我的短了一行!:D


有趣的是,如果您决定使用
fis.close()
而不是
IOUtils.closequity()
,我的代码不会改变,而“官方”代码将再增加4行。

您发布的第二个示例不会尝试/捕获
可能出现的错误新文件输入流(“…”)将返回。
在第一个示例中,您还自动捕获
FileInputStream
给出的错误


我个人会选择第一个,以获得更好的错误处理。

除非您从方法中抛出异常,否则第二个示例不会编译,因为
FileNotFoundException
是一个选中的异常

Compile.java:5: error: unreported exception FileNotFoundException; must be caught or declared to be thrown
        FileInputStream fis = new FileInputStream("some.file");
第一个示例也没有编译,因为您缺少相应的
catch
块。下面是一个编译示例

import java.io.*;
public class Compile {

    public static void main(String[] args) {
        FileInputStream fis = null;
        try {
            fis = new FileInputStream("some.file");
            // do something useful with fis

            if (fis != null) {
                fis.close();
            }
        }
        catch (FileNotFoundException fnf) {
        }
        catch (IOException ioe) {
        }
        finally {
        }
    }
}
使用:


为了回答为什么推荐模式是这样的问题,考虑下面的事实:<代码>新文件输入流(String)抛出一个您应该捕获的检查异常;

FileInputStream fis = null;
try {
    fis = new FileInputStream("somefile.txt");
} catch (FileNotFoundException e) {
    return false;
} finally {
    if (fis != null) {
        fis.close();
    }
}
允许您在与其他文件相关异常相同的try/catch块中处理它。与以下各项相比,这具有简洁性和减少嵌套的优点:

try {
    FileInputStream fis = new FileInputStream("some.file");
    try {
        // do something useful with fis
    } finally {
        fis.close();
    }
} catch (FileNotFoundException e) {
    return false;
}

如果您没有捕获异常,只是让它传播,那么您的建议就更干净了-但是考虑到IO库中大量使用检查过的异常(包括来自类构造函数),捕获和处理异常已经足够常见了,我认为,在教程中只使用一种模式是有意义的,而不是每次都向别人展示一种稍有不同的模式。

或者更好:尝试使用resources@assylias是的,这是正确的做法,谢谢。但是a)它只在Java7中工作,b)问题在于:为什么在整个文档中使用明显低劣的模式?有人会认为这应该是有原因的……如果配置了安全管理器,则构造函数也可以在运行时抛出未检查的SecurityException。@Trevortipins,这当然是真的,但这不是已检查的异常,我只是在处理编译时错误,我的代码片段不应该是完整的Java应用程序。他们不应该抓住所有的例外。你不是总能抓住所有的例外,是吗?只要在您的
main()
声明中添加一个
throws IOException
子句,我的两个示例都将按原样编译。@SnakE,我看到的大多数实际应用程序都没有向用户抛出异常的
main
。@merlin2011,大多数实际应用程序不处理
main()
中的文件。另外,当应用程序应该作为无人参与的子进程运行时,我更喜欢从main()抛出未处理的异常。通过这种方式,家长可以记录异常。如果要将此想法写入答案,将其归因于assylias是礼貌的。这是一个不错的想法,但它仅适用于Java 7,而Java 7并不总是可用的。但它并没有回答这样一个问题:为什么官方文件中使用了某种模式,而有一种更好的模式?@SnakE请参阅我更新的答案,以了解可能的原因;merlin2011:assylias的评论在我开始写我的答案时没有发布,也不是我的o的灵感来源。o@DarthAndroid同样,我可能会认为
fis.close()
也会抛出一个您应该*可能捕获的已检查异常。将其添加到两个代码段中,看看哪一个更简洁易读*可以说,您不应该这样做,但您不会在官方文档中看到这样的建议。如果
new FileInputStream()
抛出,那么
fis
将保持
null
,因此无需关闭。我问题中的第一个例子是检查
fis
并跳过
close()
。第二个示例甚至不会输入
try
。最终结果将完全相同。谢谢,但我的观点是,如果出现问题,FileInputStream会抛出异常,所以您也可以对此进行检查。