是否可以忽略使用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) {
}
}
}
}
答案 0 :(得分:24)
我在coin-dev邮件列表上找到了这个答案: http://mail.openjdk.java.net/pipermail/coin-dev/2009-April/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;
}
}
答案 1 :(得分:19)
您可以在此处使用装饰器模式静静地关闭资源:
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一起开箱即用,而且没有其他依赖关系。
答案 2 :(得分:4)
这是一个解决方案:
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
,我们的实际工作已经完成,我们并不关心资源之后出现的错误。
答案 3 :(得分:0)
我实际上并不推荐这样做,但我能想到的唯一方法是检查异常的堆栈跟踪。它来自附近的 close 方法吗?
基于 https://stackoverflow.com/a/32753924/32453,任何捕获的异常都将是来自主块的“异常”、来自关闭调用的异常,或者来自带有“抑制”关闭调用的 try 块的异常。
所以你只需要弄清楚它是否是 close 调用本身的异常,显然是 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) {
// ...
}
}
还有几点需要考虑:
一般来说,不清楚您是否想要处理来自不同于 try 块内部的 IOException
调用的 close
。如果关闭调用意味着它没有将所有数据刷新到文件中怎么办?您可能想一视同仁地处理/对待它们。
另一种选择:在块末尾附近手动关闭资源(使用自己的 try-catch)。通常允许双重关闭,因此您可以在那里捕获关闭异常。
另一种可能性:改用普通的 try-catch-finally 模式,这里有一些方法可以让它稍微不那么难看:如果您没有多个资源,Java try-finally inside try-catch pattern 可能是一个选项。