使用 try-with-resources 悄悄地关闭资源

发布于 2024-11-27 05:08:32 字数 788 浏览 1 评论 0原文

是否可以忽略使用 try-with-resources 语句关闭资源时引发的异常?

示例:

class MyResource implements AutoCloseable{
  @Override
  public void close() throws Exception {
    throw new Exception("Could not close");
  }  
  public void read() throws Exception{      
  }
}

//this method prints an exception "Could not close"
//I want to ignore it
public static void test(){
  try(MyResource r = new MyResource()){
    r.read();
  } catch (Exception e) {
    System.out.println("Exception: " + e.getMessage());
  }
}

或者我应该继续在 finally 中关闭?

public static void test2(){
  MyResource r = null;
  try {
     r.read();
  }
  finally{
    if(r!=null){
      try {
        r.close();
      } catch (Exception ignore) {
      }
    }
  }
}

Is it possible to ignore the exception thrown when a resource is closed using a try-with-resources statement?

Example:

class MyResource implements AutoCloseable{
  @Override
  public void close() throws Exception {
    throw new Exception("Could not close");
  }  
  public void read() throws Exception{      
  }
}

//this method prints an exception "Could not close"
//I want to ignore it
public static void test(){
  try(MyResource r = new MyResource()){
    r.read();
  } catch (Exception e) {
    System.out.println("Exception: " + e.getMessage());
  }
}

Or should I continue to close in a finally instead?

public static void test2(){
  MyResource r = null;
  try {
     r.read();
  }
  finally{
    if(r!=null){
      try {
        r.close();
      } catch (Exception ignore) {
      }
    }
  }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

绮筵 2024-12-04 05:08:32

我在 coin-dev 邮件列表上找到了这个答案:
http://mail.openjdk.java.net/pipermail /coin-dev/2009-4月/001503.html

<强>5。 close 方法的一些失败可以安全地忽略(例如,
关闭打开以供读取的文件)。该结构是否提供
这个?

没有。虽然这个功能看起来很有吸引力,但尚不清楚
增加复杂性是值得的。实际上,这些“无害”
“异常”很少发生,因此程序不会更健壮
如果忽略这些异常。如果你觉得你必须忽略它们,
有一个解决方法,但它并不漂亮:

static void copy(String src, String dest) throws IOException {
    boolean done = false;
    try (InputStream in = new FileInputStream(src)) {
        try(OutputStream out = new FileOutputStream(dest)) {
            byte[] buf = new byte[8192];
            int n;
            while ((n = in.read(buf)) >= 0)
                out.write(buf, 0, n);
        }
        done = true;
    } catch(IOException e) {
        if (!done)
            throw e;
    }
}

I found this answered on the coin-dev mailing list:
http://mail.openjdk.java.net/pipermail/coin-dev/2009-April/001503.html

5. Some failures of the close method can be safely ignored (e.g.,
closing a file that was open for read). Does the construct provide for
this?

No. While this functionality seems attractive, it is not clear that
it's worth the added complexity. As a practical matter these “harmless
exceptions” rarely if ever occur, so a program will be no more robust
if these exceptions are ignored. If you feel you must ignore them,
there is a workaround, but it isn't pretty:

static void copy(String src, String dest) throws IOException {
    boolean done = false;
    try (InputStream in = new FileInputStream(src)) {
        try(OutputStream out = new FileOutputStream(dest)) {
            byte[] buf = new byte[8192];
            int n;
            while ((n = in.read(buf)) >= 0)
                out.write(buf, 0, n);
        }
        done = true;
    } catch(IOException e) {
        if (!done)
            throw e;
    }
}
掐死时间 2024-12-04 05:08:32

您可以在此处使用装饰器模式来安静地关闭资源:

public class QuietResource<T extends AutoCloseable> implements AutoCloseable{
    T resource;
    public QuietResource(T resource){
        this.resource = resource;
    }
    public T get(){
        return resource;
    }
    @Override
    public void close() {
        try {
            resource.close();
        }catch(Exception e){
            // suppress exception
        }
    }  
}

我个人并不喜欢生成的语法,但这也许对您有用:

public static void test(){
    try(QuietResource<MyResource> qr = new QuietResource<>(new MyResource())){
        MyResource r = qr.get();
        r.read();
    } catch (Exception e) {
        System.out.println("Exception: " + e.getMessage());
    }
}

如果您愿意限制自己处理接口和杠杆,您可以做得更好动态代理类:

public class QuietResource<T> implements InvocationHandler {

    private T resource;

    @SuppressWarnings("unchecked")
    public static <V extends AutoCloseable> V asQuiet(V resource){
        return (V) Proxy.newProxyInstance(
                resource.getClass().getClassLoader(),
                resource.getClass().getInterfaces(),
                new QuietResource<V>(resource));
    }

    public QuietResource(T resource){
        this.resource = resource;
    }

    @Override
    public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
        if(m.getName().equals("close")){
            try {
                return m.invoke(resource, args);
            }catch(Exception e){
                System.out.println("Suppressed exception with message: " + e.getCause().getMessage());
                // suppress exception
                return null;
            }
        }
        return m.invoke(resource, args);
    }
}

然后假设您有:

public interface MyReader extends AutoCloseable{
    int read();
}

使用实际的资源类:

public class MyResource implements MyReader {

    public void close() throws Exception{
        throw new Exception("ha!");
    }

    public int read(){
        return 0;
    }
}

调用语法如下所示:

public static void test(){
    try(MyReader r = QuietResource.asQuiet(new MyResource())){
        r.read();
    } catch (Exception e) {
        System.out.println("Exception: " + e.getMessage());
    }
}

如果您想开始包含库(例如 AOP 启用程序),您可以做得更好。然而,这些解决方案可以与 JDK7 一起开箱即用,并且没有其他依赖项。

You could use a decorator pattern here to close the resource quietly:

public class QuietResource<T extends AutoCloseable> implements AutoCloseable{
    T resource;
    public QuietResource(T resource){
        this.resource = resource;
    }
    public T get(){
        return resource;
    }
    @Override
    public void close() {
        try {
            resource.close();
        }catch(Exception e){
            // suppress exception
        }
    }  
}

I'm not personally a fan of the resulting syntax, but maybe this works for you:

public static void test(){
    try(QuietResource<MyResource> qr = new QuietResource<>(new MyResource())){
        MyResource r = qr.get();
        r.read();
    } catch (Exception e) {
        System.out.println("Exception: " + e.getMessage());
    }
}

You can do better if you're willing to limit yourself to dealing with interfaces and leverage a Dynamic Proxy Class:

public class QuietResource<T> implements InvocationHandler {

    private T resource;

    @SuppressWarnings("unchecked")
    public static <V extends AutoCloseable> V asQuiet(V resource){
        return (V) Proxy.newProxyInstance(
                resource.getClass().getClassLoader(),
                resource.getClass().getInterfaces(),
                new QuietResource<V>(resource));
    }

    public QuietResource(T resource){
        this.resource = resource;
    }

    @Override
    public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
        if(m.getName().equals("close")){
            try {
                return m.invoke(resource, args);
            }catch(Exception e){
                System.out.println("Suppressed exception with message: " + e.getCause().getMessage());
                // suppress exception
                return null;
            }
        }
        return m.invoke(resource, args);
    }
}

Then assuming you have:

public interface MyReader extends AutoCloseable{
    int read();
}

With an actual resource class:

public class MyResource implements MyReader {

    public void close() throws Exception{
        throw new Exception("ha!");
    }

    public int read(){
        return 0;
    }
}

Calling syntax would look like:

public static void test(){
    try(MyReader r = QuietResource.asQuiet(new MyResource())){
        r.read();
    } catch (Exception e) {
        System.out.println("Exception: " + e.getMessage());
    }
}

You can do better than this if you want to start including libraries, like AOP enablers. These solutions, however, will work out of the box with JDK7 and no other dependencies.

抠脚大汉 2024-12-04 05:08:32

这是一种解决方案:

    boolean ok=false;
    try(MyResource r = new MyResource())
    {
        r.read();
        ok=true;
    }
    catch (Exception e)
    {
        if(ok)
            ; // ignore
        else
            // e.printStackTrace();
            throw e;
    }

如果 ok==true 并且我们收到异常,它肯定来自 close()

如果ok==false,则e来自read()或构造函数。 close() 仍会被调用,并可能抛出 e2,但 e2 无论如何都会被抑制。

无需经过这样的分析,代码就具有很好的可读性。直观地说,如果ok==true,我们真正的工作就完成了,我们并不真正关心之后会出现什么关于资源的错误。

This is one solution:

    boolean ok=false;
    try(MyResource r = new MyResource())
    {
        r.read();
        ok=true;
    }
    catch (Exception e)
    {
        if(ok)
            ; // ignore
        else
            // e.printStackTrace();
            throw e;
    }

If ok==true and we got an exception, it definitely comes from close().

If ok==false, e comes from read() or constructor. close() will still be called and may throw e2, but e2 will be suppressed anyway.

The code is quite readable without going through such analysis. Intuitively it says, if ok==true, our real work is done, and we don't really care what errors come after that regarding the resource.

对岸观火 2024-12-04 05:08:32

我实际上并不推荐这样做,但我能想到的唯一方法是检查异常的堆栈跟踪。它是否来自附近的 close 方法?

基于 https://stackoverflow.com/a/32753924/32453 任何捕获的异常都将是来自主块、关闭调用的异常或带有“抑制”关闭调用的 try 块的异常。

因此,您只需弄清楚它是否是 close call 本身的异常,这显然是 catch 行:

try (Resource myResource = new Resource()) {

} catch (IOException mightBeFromClose) {
  int currentLine = new Throwable().getStackTrace()[0].getLineNumber();
  int lineOfCatch = currentLine - 1;
  String currentFilename = new Throwable().getStackTrace()[0].getFileName();
  boolean exceptionWasFromClose = Stream.of(mightBeFromClose.getStackTrace()).anyMatch(l -> l.getFileName().equals(currentFilename) && l.getLineNumber() == lineOfCatch);
  if (exceptionWasFromClose) {
    // ...
  }
}

还有一些需要考虑的事情:

一般来说,不清楚您是否“ d 希望处理来自 close 调用的 IOException ,而不是来自 try 块内部的调用。如果关闭调用意味着它没有将所有数据刷新到文件中怎么办?您可能想以同样的方式处理/对待它们。

另一种选择:在块末尾附近手动关闭资源(使用自己的 try-catch)。通常允许双重关闭,因此您可以在那里捕获关闭异常。

另一种可能性:使用普通的 try-catch-finally 模式代替,这里有一些方法可以让它稍微不那么难看: Java try-finally inside try如果您没有多个资源,-catch 模式 可能是一个选项。

I don't actually recommend this but the only way I can imagine to do this is examining the Exception's stacktrace. Did it come from within a nearby close method?

Based on https://stackoverflow.com/a/32753924/32453 any caught exception will either be an "exception" from the main block, an exception from the close calls, or an exception from the try block with "suppressed" close calls.

So you just have to figure out if it's an exception from the close call itself, which is the line of the catch, apparently:

try (Resource myResource = new Resource()) {

} catch (IOException mightBeFromClose) {
  int currentLine = new Throwable().getStackTrace()[0].getLineNumber();
  int lineOfCatch = currentLine - 1;
  String currentFilename = new Throwable().getStackTrace()[0].getFileName();
  boolean exceptionWasFromClose = Stream.of(mightBeFromClose.getStackTrace()).anyMatch(l -> l.getFileName().equals(currentFilename) && l.getLineNumber() == lineOfCatch);
  if (exceptionWasFromClose) {
    // ...
  }
}

A few more things to consider:

In general it is not clear if you'd want to handle IOException from a close call different than one from inside the try block. What if a close call implies it didn't flush all data out to the file? You may want to handle/treat them all the same.

Another option: close the resource manually near the end of the block (with its own try-catch). Double-close is typically allowed so you could catch the close Exception there.

Another possibility: use normal try-catch-finally pattern instead, here are some ways to make it slightly less ugly: Java try-finally inside try-catch pattern might be an option if you don't have multiple resources.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文