我正在构建我的构造函数中的一些对象,这些对象需要在完成后调用.close()。如果在该序列中有一个对象抛出异常,那么如何清理到目前为止已经分配的对象。这提出了一个问题,即调用函数(即使它使用try-finally)永远不会得到一个引用来调用.close作为对象永远不会完成构造。
我有过的想法:
示例代码:
class MyClass implements AutoCloseable {
private EarthConnection earth;
private SolarConnection solar;
public MyClass() {
earth = new EarthConnection();
solar = new SolarConnection(); // exception thrown by this connection
}
public close() {
if (earth != null) {
earth.close();
}
if (solar != null) {
solar.close();
}
}
}
// Caller
try (MyClass myClass = new MyClass()) {
// do work - note if MyClass wasn't fully constructored it can't call the close method on it.
}
在上面的示例中,如果SolarConnection抛出异常,如何清理分配的EarthConnection?
答案 0 :(得分:1)
try-with-resources怎么样(假设您使用的是Java 7或更高版本)?如果您在try部分中创建对象,那么它将自动关闭而不会捕获异常。这种方法的唯一缺点是你仍然可以在try部分之外创建对象,然后它将不会被关闭。
答案 1 :(得分:0)
依赖于从构造函数中捕获异常是不好的做法。我建议在那里使用工厂方法一个catch异常:try ... catch with recources
答案 2 :(得分:0)
保持简单,一个类应该对自己的状态负责,而不是MyClass
做其他类的资源。这还将确保将来MyClass
更少的代码更改,以及其他新类。
IMO,你可以尝试一下:
class EarthConnection implements AutoCloseable {
@Override
public void close(){
/* TO DO */
}
}
class SolarConnection implements AutoCloseable {
@Override
public void close(){
/* TO DO */
}
}
class MyClass {
private EarthConnection earth;
private SolarConnection solar;
public MyClass(EarthConnection earth, SolarConnection solar) {
this.earth = earth;
this.solar = solar;
}
/* TO DO */
}
try(EarthConnection earth = new EarthConnection()){
try(SolarConnection solar = new SolarConnection()){ /* exception thrown by this connection*/
MyClass myClass = new MyClass(earth,solar);
/* TO DO */
}
}
答案 3 :(得分:0)
我个人会这样做:而不是构造函数,一个静态方法来包装丑陋的代码,让你的构造函数变得简单;
在你的情况下,你不必捕获所有异常,只有那些在第一次初始化之后出现的异常:solar
是在earth
之后创建的(在宇宙中可能不是这种情况:) ),所以只有它失败了,你才需要彻底清除earth
。
您不能在此处使用 try-with-resources ,因为您将关闭不是您想要的资源。
class MyClass implements AutoCloseable {
private EarthConnection earth;
private SolarConnection solar;
private MyClass(EarthConnection earth, SolarConnection solar) {
earth = new EarthConnection();
solar = new SolarConnection(); // exception thrown by this connection
}
public static MyClass newMyClass() {
EarthConnection earth = new EarthConnection();
try {
SolarConnection solar = new SolarConnection();
return new MyClass(earth, solar);
} catch (SolarException e) {
earth.close(); // may throw, you can ignore it.
throw e;
}
}
...
}
如果您有earth
以及solar
,例如mars
和venus
,则可能需要使用包含List
的类AutoCloseable
class MyClass implements AutoCloseable {
private final EarthConnection earth;
private final VenusConnection venus;
private final MarsConnection mars;
private final SolarConnection solar;
private final AutoCloseable cl;
private MyClass(
final EarthConnection earth,
final VenusConnection venus,
final MarsConnection mars,
final SolarConnection solar,
final AutoCloseable ac
) {
this.earth = earth;
this.venus = venus;
this.mars = mars;
this.solar = solar;
this.cl = cl;
}
public static MyClass newMyClass() {
AutoCloseables cl = new AutoCloseables<>();
try {
EarthConnection earth = cl.register(new EarthConnection());
VenusConnection venus = cl.register(new VenusConnection ());
MarsConnection mars = cl.register(new MarsConnection());
SolarConnection solar = cl.register(new SolarConnection());
return new MyClass(earth, venus, mars, solar, cl);
} catch (EarthException | VenusException | MarsException | SolarException e) {
cl.close();
throw e; // or new MyClassException(e);
}
}
@Override
public void close() {
cl.close();
}
,但是你需要以相反的顺序关闭对象(非常类似于以相反的构造顺序调用C ++析构函数)。
class AutoCloseables {
private final List<AutoCloseable> list;
public <E extends AutoCloseable> E register(E ac) {list.add(ac); return ac;}
@Override
public void close() {
Collections.reverse(list); // destroy in reverse order
for (AutoCloseable ac : list) {
try {ac.close();}
catch (Exception e) {
// IGNORED or you may use supressedException https://docs.oracle.com/javase/7/docs/api/java/lang/Throwable.html#addSuppressed(java.lang.Throwable)
}
}
}
}
使用:
spark-shell --packages maven-coordinates of the package
答案 4 :(得分:0)
抓住Exception
并重新抛出应该这样做。这允许调用者查看原始异常,但允许类中所需的清理来关闭资源。
public MyClass() {
try {
earth = new EarthConnection();
solar = new SolarConnection();
} catch(Exception e) {
close();
throw e;
}
}