Java 从try中捕获对象构造过程中的错误,并将资源与主体分开 总结

Java 从try中捕获对象构造过程中的错误,并将资源与主体分开 总结,java,try-with-resources,autocloseable,Java,Try With Resources,Autocloseable,我有一个可关闭类型,CloseableClass,它可以在它的构造函数、方法中甚至在close中抛出一个IOError。我想使用try-with-resources,但在构建过程中处理错误的方式与在使用过程中处理错误的方式不同(使用包括清理)。更好的是,我想编写可维护的代码 假设您希望构造一个closeable类实例,并将其与try with resources语句一起使用。它可以在其构造函数和try with resources主体中使用的方法中抛出IOException: import j

我有一个可关闭类型,
CloseableClass
,它可以在它的构造函数、方法中甚至在
close
中抛出一个IOError。我想使用try-with-resources,但在构建过程中处理错误的方式与在使用过程中处理错误的方式不同(使用包括清理)。更好的是,我想编写可维护的代码


假设您希望构造一个closeable类实例,并将其与try with resources语句一起使用。它可以在其构造函数和try with resources主体中使用的方法中抛出
IOException

import java.io.Closeable;
import java.io.IOException;
import java.util.Random;

public class CloseableClass implements Closeable {
    public CloseableClass() throws IOException {
        if (new Random().nextBoolean()) {
            throw new IOException();
        }
    }

    public void internetStuff() throws IOException {
        if (new Random().nextBoolean()) {
            throw new IOException();
        }
    }

    public void close() throws IOException {
        if (new Random().nextBoolean()) {
            throw new IOException();
        }
    }

    public static void main(String[] args) {
        try (CloseableClass closeable = new CloseableClass()) {
            closeable.internetStuff();
        }
        catch (IOException e) {
            System.out.println("Bad error!");
        }
    }
}
假设您希望分别处理构造函数和主体中抛出的错误。有没有一种受支持的方法可以做到这一点?在Python中,我将执行以下操作:

try:
    closeable = CloseableClass()
except IOException:
    print("Constructor error")
    return

try:
    with closeable:
        closeable.internet_stuff()
except IOException:
    print("Body error")
但在Java中,如果不为对象指定第二个名称,则不能:

CloseableClass closeable_;

try {
    closeable_ = new CloseableClass();
}
catch (IOException e) {            
    System.out.println("Constructor error!");
    return;
}

try (CloseableClass closeable = closeable_) {
    closeable.internetStuff();
}
catch (IOException e) {
    System.out.println("Body error!");
}
有人告诉我,这是“无法维护的代码”,主要是因为使用了
closeable\uu
,我对此表示同意。我希望避免使用try finally,因为这样您在模拟它时会遇到更糟糕的问题:

CloseableClass closeable;

try {
    closeable = new CloseableClass();
}
catch (IOException e) {            
    System.out.println("Constructor error!");
    return;
}

try {
    closeable.internetStuff();
}
catch (IOException e) {
    try {
        closeable.close();
    }
    catch (IOException ignore) {
        // Already dealing with this
    }

    System.out.println("Body error!");
}
finally {
    try {
        closeable.close();
    }
    catch (IOException e) {
        System.out.println("Body error!");
    }
}
注意,这需要对
close
的第二次调用成为no-op,测试类不遵守该操作(注意
AutoCloseable
不需要,尽管
Closeable
需要)。如果
close
不能抛出,但抛出的次数不多,这会稍微好一点

基本上问题是

  • 关闭
    可以抛出
  • 在处理
    IOException
    之前关闭,以防止打印两次
    “正文错误!”
  • 如何使用try with resources中的多个初始值设定项还不清楚
  • 你最终还是复制了代码
我是被迫接受“不可维护的代码”还是忽略了一个处理这个问题的好方法?

“请注意,这需要第二次调用关闭才能成为禁止操作”-不,您不需要
catch
块中的
close()
,因为
最后将始终执行
块。如果在
catch
块中使用类似于
System.exit()的调用终止JVM,则只能在
catch
块中使用
close()
。通常,您将从
catch
时钟向调用者抛出
Exception
,但您将在
最后执行清理
阻塞大部分时间

使用资源进行尝试更好,但您可以使用抛出的
异常的类型和描述来破译错误的内容和位置

编辑

据我所知,我建议:

1) 尝试使用资源:

try(Resource resource = new Resource()){
    // use resource.
}catch(Exception e){
    // handle exception.
    // OR better to throw exception to caller.
    throw e;
}
2) 传统风格:

Resource resource = null;
try{
    resource = new Resource();
    // use resource
}catch(Exception e){
    // handle exception.
    // OR better to throw exception to caller.
    throw e;
} finally {
   if(resource != null){
       try{
           resource.close();
       } catch(Exception e){
           // most of time you wont or cant do anything here.
       }
   }
}

一种解决方案是定义一种方法,将初始化错误封装在自定义异常类型中,然后使用该方法确定错误发生的时间

private CloseableClass createCloseable() throws CloseableCreateException{
    try {
        return new CloseableClass();
    } except (IOException e) {
        throw new CloseableCreateException(e);
    }
}
另一个简单但有点不雅观的解决方案是使用布尔标志:

boolean init = true;
try (CloseableClass closeable = new CloseableClass()) {
    init = false;
    closeable.internetStuff();
} catch (IOException e) {
    if (init) {
        System.out.println("Constructor error!");
    } else {
        System.out.println("Body error!");
    }
}

是的,但是如果我不尝试在
捕获中关闭,我最终会冒打印
“Body error!”
两次的风险(如果
尝试{…}
关闭
抛出)。我真正的用例,
Socket
似乎抛出了很多裸露的
IOException
s,因此我不能只选择更具体的异常来捕获。您仍然有可能打印“Body Error!”,因为catch块将打印,并且可能最终也会被阻止。记住,finally块将始终执行,无论您是从try或catch返回异常,还是从这些返回,但finally块都将始终执行。使用您添加的代码,我如何从类的构造函数和
try
的主体中区分错误?从
异常的类型和描述中。像创建
FileReader
一样,可能会抛出
FileNotFoundException
,这是来自文件读取器的构造函数,而不是来自readig file。如果无法区分异常,则需要尝试{}catc(){}每行代码。是的,您也可以在try中关闭资源,并在try中使其为null,以防止最后再次关闭。但我更喜欢最后收尾。
boolean init = true;
try (CloseableClass closeable = new CloseableClass()) {
    init = false;
    closeable.internetStuff();
} catch (IOException e) {
    if (init) {
        System.out.println("Constructor error!");
    } else {
        System.out.println("Body error!");
    }
}