如何简化/重复使用此异常处理代码

5

我经常写出以下这样的代码:

BufferedWriter w = null; // Or any other object that throws exceptions and needs to be closed
try {
    w = new BufferedWriter(new FileWriter(file));
    // Do something with w
} catch (IOException e) {
    e.printStackTrace();
} finally {
    if (w != null) {
        try {
            w.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

通常涉及到一个会抛出异常并需要关闭的对象,而关闭它也可能会抛出异常。

我在想上面的代码是否可以简化或以任何方式重用。


我因这个问题学到了很多Java。 :D - hpique
8个回答

6
如果您不想编写finally块中的关闭代码,可以查看Lombok项目
与其编写常规的

代码,
public class CleanupExample {
  public static void main(String[] args) throws IOException {
  InputStream in = new FileInputStream(args[0]);
  try {
    OutputStream out = new FileOutputStream(args[1]);
    try {
      byte[] b = new byte[10000];
      while (true) {
         int r = in.read(b);
         if (r == -1) break;
         out.write(b, 0, r);
      }
    } finally {
        out.close();
      }
  } finally {
     in.close();
    }
  }
}

使用Lombok,你可以写成这样:
public class CleanupExample {
  public static void main(String[] args) throws IOException {
    @Cleanup InputStream in = new FileInputStream(args[0]);
    @Cleanup OutputStream out = new FileOutputStream(args[1]);
    byte[] b = new byte[10000];
    while (true) {
      int r = in.read(b);
      if (r == -1) break;
      out.write(b, 0, r);
    }
   }
 }

这样更易读,并且它生成了正确的关闭流的方式。这适用于所有的Closeable接口。


5
我通常会将finally块的内容放在一个帮助程序中。就像这样。
void close(Closeable c) {
    if (c != null) {
        try {
            c.close();
        } catch (IOException e) {
            // perform logging or just ignore error
        }
    }
}

Closeable 接口被许多类(输入流、数据库连接等)实现,因此它是一种通用的帮助程序。


哦!像Closeable这样的东西正是我正在寻找的。 - hpique

4

是的,从Java 1.5开始就有了Closeable接口。您可以拥有一个静态方法,用于关闭任何Closeable类型。

  public static void closeIO(Closeable closeable){
      if (closeable != null) {
        try {
            closeable.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

1
或者可以重用 Guava 中的 Closeables - whiskeysierra

3

Java 7支持使用try-with-resource。查看此链接以获取更多信息。

以下是相关文本和代码示例:

使用Java 7中的新try-with-resource语言功能,您可以将流参数作为try结构的一部分声明,编译器会为您自动生成代码,自动且清晰地管理这些资源。

private static void customBufferStreamCopy(File source, File target) {
    try (InputStream fis = new FileInputStream(source);
        OutputStream fos = new FileOutputStream(target)){

        byte[] buf = new byte[8192];

        int i;
        while ((i = fis.read(buf)) != -1) {
            fos.write(buf, 0, i);
        }
    }
    catch (Exception e) {
        e.printStackTrace();
    }
}

1
好的,但也许他不想等到2011年中期才能使用JDK 1.7,对吧? - Romain Linsolas
我相信任何客户都会理解的。 :P - hpique

1

我倾向于同意其他人提供一个接受 Closeable 的方法,但由于维护非常长寿命的程序,我使用的解决方案略有不同。基本上,它采用一个 OutputStream 来提供灵活性。

public class IOHandler {

  private IOHandler();

  public static void close(OutputStream out, Closeable c) {
    if (c != null) {
      try {
        c.close();
    } catch (IOException e) {
        out.print(c.printStackTrace().getBytes());
    }
  }

}

主要的优点是您可以以多种方式调用它,无需专门的工具来处理将日志异常记录到stderr、stdout和文件中。
IOHandler.close(System.out, openFile);
IOHandler.close(System.err, openFile);
IOHandler.close(logFile, openFile);

除了这个新增的功能,它基本上和其他人提供的解决方案一样。


1

我发现最好不要在同一个块中同时使用try-catch和finally。通常最好将其分别放在try-catch块和try-finally块中。

try {
    BufferedWriter w = new BufferedWriter(new FileWriter(file)); // Or any other object that throws exceptions and needs to be closed
    try {
        // Do something with w
    } finally {
        w.close();
    }
} catch (IOException e) {
    e.printStackTrace();
}

这也避免了任何需要对w进行空值检查的需求。


当底层流仍在创建时,如果外部流在构造函数中抛出异常,您将会遇到问题。然后,底层流将不会关闭。 - Fabian Barney
@Fatal new BufferedWriter(Writer) 不会抛出任何已检查异常。即使它抛出异常,问题也存在于调用new BufferedWriter(new FileWriter(file))中,正如问题所描述的那样,而不是在处理异常的这种方法中。为了缓解这个问题,您必须将new FileWriter分配给一个单独的变量,并关闭它。 - ILMTitan
你在使用 BufferedWriter 上是正确的,但这里讨论的话题更为普遍,而且您的建议也很笼统。由于 close() 协议,您不必分别分配和关闭 FileWriter。hgpc 在他的帖子中做得很对。我知道所有常见的库,例如 Apache Commons 都是这样做的 - 并非偶然。因此,被接受的答案是最好的 - 它只是改善了正确的做法,并减少了样板文件。即使在这种特殊情况下可能有效,这并不是我会推荐的。 - Fabian Barney
@Fatal,我不明白你在说什么。除了在两个catch子句中能够执行不同的操作之外,这两种方法在功能上是相同的。你是在说它们不同(如果是的话,怎么不同?),还是说通常的方式更清晰? - ILMTitan
@Fatal 让我们在聊天室里继续这个讨论 - ILMTitan
显示剩余2条评论

0

用一个方法来写它...

BuffereWriter getMyWriter()
{

// your code....

return w;
}

0

这里可以应用模板方法模式:

public class FileTemplate {
    public void write(File file, WriteCallback c) {
        BufferedWriter w = null; // Or any other object that throws exceptions and needs to be closed 
        try { 
            w = new BufferedWriter(new FileWriter(file)); 
            c.writeFile(w); 
        } catch (IOException e) { 
            e.printStackTrace(); 
        } finally { 
            if (w != null) { 
                try { 
                    w.close(); 
                } catch (IOException e) { 
                    e.printStackTrace(); 
                } 
            } 
        }
    }
}

public interface WriteCallback {
    public void writeFile(BufferedWriter w) throws IOException;
}

.

new FileTemplate().write(file, new WriteCallback() {
    public void writeFile(BufferedWriter w) { ... }
});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接