Java资源关闭

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

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(); } 

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

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

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

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

注意:

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

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

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

Yishai发布了关闭流的好模式 (关闭可能会抛出另一个exception)。

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

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

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

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

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

 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) {} } 

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

 try (BufferedReader br = new InputStreamReader(new URL("www.example.com").openStream())) { String response = br.readLine(); } catch (Exception e) { //exception handling } 

也可以看看:

  • Java IO教程
  • Java中的C#“using”关键字
  • 如何使用URLConnection
 BufferedReader br = null; 

你在没有赋值的情况下声明一个变量( null不计算 – 在这种情况下它是无用的赋值)。 这是Java中的代码“气味”(参考有效Java ;有关变量声明的更多代码完成 )。

 }finally{ br.close(); isr.close(); } 

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

 isr = new InputStreamReader(connection.getInputStream()); 

如果InputStreamReader构造函数抛出任何类型的运行时exception(通常不太可能)事件,则不会关闭来自连接的流。

利用Closeable接口减少冗余。

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

 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(); } 

注意:

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

前段时间,我花了一些时间考虑如何在出现问题时避免泄漏资源/数据 。

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

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

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

是的,这很丑……现在。 我认为有计划在Java中进行自动资源管理 。

我正在使用apache commons IO,正如其他人所建议的那样,主要是IOUtils.toString(InputStream)和IOUtils.closeQuietly(InputStream) :

 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); } } 

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

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

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

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

 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 } } 

getEncoding需要检查连接的结果getContentEncoding()getContentType()以确定网页的编码; 您的代码只使用平台的默认编码,这可能是错误的。

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

 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(); } } 

正如McDowell指出的那样,如果new InputStreamReader抛出,您可能需要关闭输入流。

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

 try(Resource resource = acquire()) { use(resource); reuse(resource); } 
Interesting Posts