Java资源关闭

问题描述 投票:9回答:8

我正在编写一个连接到网站并从中读取一行的应用程序。我是这样做的:

try{
        URLConnection connection = new URL("www.example.com").openConnection();
        BufferedReader rd = new BufferedReader(new InputStreamReader(connection.getInputStream()));
        String response = rd.readLine();
        rd.close();
    }catch (Exception e) {
        //exception handling
    }

好吗?我的意思是,我在最后一行关闭了BufferedReader,但我没有关闭InputStreamReader。我应该从connection.getInputStream创建一个独立的InputStreamReader,还是从独立的InputStreamReader创建一个BufferedReader,而不是关闭所有两个读者?我认为最好将结束方法放在finally块中,如下所示:

InputStreamReader isr = null;
BufferedReader br = null;
try{
    URLConnection connection = new URL("www.example.com").openConnection();
    isr = new InputStreamReader(connection.getInputStream());
    br = new BufferedReader(isr);
    String response = br.readLine();
}catch (Exception e) {
    //exception handling
}finally{
    br.close();
    isr.close();
}

但它很难看,因为关闭方法可以抛出异常,所以我必须处理或抛出它。

哪种解决方案更好?或者什么是最好的解决方案?

java url exception-handling inputstream
8个回答
9
投票

Java中资源获取和发布的一般习惯是:

final Resource resource = acquire();
try {
    use(resource);
} finally {
    resource.release();
}

注意:

  • try应立即跟进收购。这意味着你不能将它包装在装饰器中并保持安全(并删除空格或将东西放在一行上没有帮助:)。
  • 每个qazxsw opi一个版本,否则它不会是例外安全。
  • 避免使用finally,使用null。否则你会有杂乱的代码和NPE的潜力。
  • 通常不需要关闭装饰器,除非它具有与之相关的其他资源。但是,您通常需要刷新输出,但在异常情况下避免使用它。
  • 异常应该传递给调用者,或者从周围的final块中捕获(Java会让你误入歧途)。

你可以用try抽象这个废话,所以你不必重复自己(只是写了很多样板)。


3
投票

关闭BufferedReader就足够了 - 这也关闭了底层读者。

Execute Around idiom用于关闭流(关闭可能会抛出另一个异常)。


3
投票

好吗?我的意思是,我在最后一行关闭了BufferedReader,但我没有关闭InputStreamReader。

除了它应该在Yishai posted a nice pattern完成(以便确保关闭,即使在例外的情况下),它是好的。 Java IO类使用装饰器模式。关闭将被委托给底层流。

但它很难看,因为关闭方法可以抛出异常,所以我必须处理或抛出它。

当关闭抛出异常时,通常只意味着另一方已被关闭或删除,这完全超出您的控制范围。您可以在最高日志或忽略它。在一个简单的应用程序中,我会忽略它。在关键任务应用程序中,我会记录它,只是为了确定。

在一个坚果中,您的代码可以重写为:

finally

在Java 7中,将有自动资源处理,这将使您的代码简洁如下:

BufferedReader br = null;
try {
    URLConnection connection = new URL("www.example.com").openConnection();
    br = new BufferedReader(new InputStreamReader(connection.getInputStream()));
    String response = br.readLine();
}catch (Exception e) {
    //exception handling
}finally{
    if (br != null) try { br.close(); } catch (IOException ignore) {}
}

也可以看看:

  • try (BufferedReader br = new InputStreamReader(new URL("www.example.com").openStream())) { String response = br.readLine(); } catch (Exception e) { //exception handling }
  • Java IO tutorial
  • C# "using" keyword in Java

3
投票
How to use URLConnection

你在没有赋值的情况下声明一个变量(BufferedReader br = null; 不计算 - 在这种情况下它是无用的赋值)。这是Java中的代码“气味”(ref null; Effective Java,有关变量声明的更多信息)。

Code Complete

首先,你只需要关闭最顶级的流装饰器(}finally{ br.close(); isr.close(); } 将关闭br)。其次,如果isr抛出异常,则不会调用br.close(),因此这不是声音代码。在某些异常情况下,您的代码将使用isr.close()隐藏原始异常。

NullPointerException

如果isr = new InputStreamReader(connection.getInputStream()); 构造函数抛出任何类型的运行时异常(不可否认)事件,则不会关闭来自连接的流。

利用InputStreamReader接口来减少冗余。

以下是我编写代码的方法:

Closeable

注意:

  • 无论抛出什么样的异常(运行时或检查)或者在哪里,代码都不会泄漏流资源
  • 没有挡块;异常应该传递到代码可以做出关于错误处理的明智决定的地方;如果这个方法是正确的地方,你可以使用try / catch包围上述所有方法

前段时间,我花了一些时间思考如何URLConnection connection = new URL("www.example.com").openConnection(); InputStream in = connection.getInputStream(); Closeable resource = in; try { InputStreamReader isr = new InputStreamReader(in); resource = isr; BufferedReader br = new BufferedReader(isr); resource = br; String response = br.readLine(); } finally { resource.close(); }


1
投票

我认为将结束方法放在finally块中会更好

是的,总是。因为可能发生异常并且资源未正确释放/关闭。

您只需要关闭最外层的阅读器,因为它将负责关闭任何封闭的阅读器。

是的,这很难......现在。我认为有计划在Java中使用avoid leaking resources/data when things go wrong


1
投票

正如其他人所建议的那样,我会使用apache commons IO,主要是automatic resource managementIOUtils.toString(InputStream)

IOUtils.closeQuietly(InputStream)

0
投票

对于java.io中的任何嵌套流和读取器,您不需要多个close语句。很难在一个单独的事件中关闭多个东西 - 大多数构造函数都可以抛出异常,因此您将尝试关闭尚未创建的东西。

如果要关闭流,无论读取是否成功,那么您需要输入到finally。

不要为变量赋值null,然后比较它们以查看事先是否发生了事情;而是构建您的程序,以便只有在不抛出异常时才能到达关闭流的路径。除了用于迭代for循环的变量之外,变量不需要改变值 - 我倾向于将所有内容标记为final,除非有其他要求。在你的程序周围有标记,告诉你如何获得当前正在执行的代码,然后根据这些标志改变行为,这是一种程序性(甚至是结构化的)编程风格。

如何嵌套try / catch / finally块取决于您是否要以不同方式处理不同阶段抛出的异常。

public String readFromUrl(final String url) {

    InputStream stream = null; // keep this for finally block

    try {
        stream = new URL(url).openConnection().getInputStream();  // don't keep unused locals
        return IOUtils.toString(stream);
    } catch (final IOException e) {
        // handle IO errors here (probably not like this)
        throw new IllegalStateException("Can't read URL " + url, e);
    } finally {
        // close the stream here, if it's null, it will be ignored
        IOUtils.closeQuietly(stream);
    }

}

private static final String questionUrl = "http://stackoverflow.com/questions/3044510/"; public static void main ( String...args ) { try { final URLConnection connection = new URL ( args.length > 0 ? args[0] : questionUrl ).openConnection(); final BufferedReader br = new BufferedReader ( new InputStreamReader ( connection.getInputStream(), getEncoding ( connection ) ) ); try { final String response = br.readLine(); System.out.println ( response ); } catch ( IOException e ) { // exception handling for reading from reader } finally { // br is final and cannot be null. no need to check br.close(); } } catch ( UnsupportedEncodingException uee ) { // exception handling for unsupported character encoding } catch ( IOException e ) { // exception handling for connecting and opening reader // or for closing reader } } 需要检查连接的getEncodinggetContentEncoding()的结果,以确定网页的编码;您的代码只使用平台的默认编码,这可能是错误的。

你的例子在结构化方面是不寻常的,因为它非常程序化;通常,您会在较大的系统中分离打印和检索,并允许客户端代码处理任何异常(或者有时捕获并创建自定义异常):

getContentType()

正如McDowell指出的那样,如果public static void main ( String...args ) { final GetOneLine getOneLine = new GetOneLine(); try { final String value = getOneLine.retrieve ( new URL ( args.length > 0 ? args[0] : questionUrl ) ); System.out.println ( value ); } catch ( IOException e ) { // exception handling for retrieving one line of text } } public String retrieve ( URL url ) throws IOException { final URLConnection connection = url.openConnection(); final InputStream in = connection.getInputStream(); try { final BufferedReader br = new BufferedReader ( new InputStreamReader ( in, getEncoding ( connection ) ) ); try { return br.readLine(); } finally { br.close(); } } finally { in.close(); } } 抛出,你可能需要关闭输入流。


0
投票

在Java 8的范围内,我会使用相同的:

new InputStreamReader
© www.soinside.com 2019 - 2024. All rights reserved.