考虑以下场景:我有一个对象池,I(1)借用该对象池,(2)对该对象执行某些操作,然后(3)必须将其返回到池中。挑战在于前两个步骤可能会抛出已检查的异常,步骤(1)甚至会抛出一个简单的Exception
。
让我向您展示我目前使用的代码:
MyObject objectFromPool = null;
try {
objectFromPool = pool.borrowObject(); // step (1), throws plain Exception
objectFromPool.doSomething(); // step (2), throws FailedToDoSomethingException
} catch (FailedToDoSomethingException e) {
throw new MyCustomRuntimeException(e);
} catch (Exception e) {
// Now what? Did objectFromPool.doSomething() throw this exception or pool.borrowObject()?
} finally {
if (objectFromPool != null) {
pool.returnObject(objectFromPool); // step (3)
}
}
如何处理普通Exception
的任何建议?我不希望这段代码抛出一个已检查的异常,因为调用者无论如何都不知道如何处理它。但我仍然希望区分pool.borrowObject()
和objectFromPool.doSomething()
的例外,因为前者表示“技术”例外而后者表示“业务”问题。
注意:我既不控制pool.borrowObject()
也不控制objectFromPool.doSomething()
的代码。两者都来自外部库,我不知道他们可能抛出什么样的RuntimeException
。
答案 0 :(得分:1)
如果您想知道哪个方法调用引发了异常,请从每个方法中抛出不同的异常。
根据您的评论,doSomething
似乎只会引发FailedToDoSomethingException
。如果这是真的,那么到达第二个捕获区块(catch (Exception e)
)的唯一方法是pool.borrowObject()
投掷Exception
。
如果两个方法调用都抛出不是Exception
的{{1}},则必须具有更多特定的catch块,以便以不同方式处理由不同方法抛出的异常。如果要抛出基类FailedToDoSomethingException
类的实例,则应考虑抛出更具体的自定义异常类实例。
答案 1 :(得分:1)
我认为更好的方法是将可疑的Exception封装到一个特定的异常中,该异常将落入最终的catch中并最终:
MyObject objectFromPool = null;
try {
try {
objectFromPool = pool.borrowObject(); // step (1), throws plain Exception
} catch (Exception e) {
// Encapsulate into an specific exception type:
throw new MyOwnException(e);
}
objectFromPool.doSomething(); // step (2), throws FailedToDoSomethingException
} catch (FailedToDoSomethingException e) {
throw new MyCustomRuntimeException(e);
} catch (MyOwnException e) {
...
} finally {
if (objectFromPool != null) {
pool.returnObject(objectFromPool); // step (3)
}
}
通过这种方式,您可以保留原始捕获并最终保留算法,毫不怀疑每个异常的来源。
还有一件事:请记住,Exception甚至可能是RuntimeException。您是否也有兴趣捕获RuntimeExceptions?如果没有,请添加一个额外的捕获来让它们传播:
try {
objectFromPool = pool.borrowObject(); // step (1), throws plain Exception
} catch (RuntimeException e) {
// Let it propagate:
throw e;
} catch (Exception e) {
// Encapsulate into an specific exception type:
throw new MyOwnException(e);
}
答案 2 :(得分:0)
如果两种方法都抛出普通Exception
,你可以通过将第二种方法包装到新的try / catch块来区分它们:
try {
objectFromPool = pool.borrowObject();
try {
objectFromPool.doSomething();
} catch (Exception ex){
// do something
}
} catch (Exception e) {
// pool.borrowObject() throw this e
} finally ...
但如果objectFromPool.doSomething()
只能投出FailedToDoSomethingException
,请按照 Eran 的回答
答案 3 :(得分:0)
怎么样:
MyObject objectFromPool = null;
try{
try {
objectFromPool = pool.borrowObject(); // step (1), throws plain Exception
} catch (Exception e) {
// pool.borrowObject has thrown exception
}
try {
objectFromPool.doSomething(); // step (2), throws FailedToDoSomethingException
} catch (FailedToDoSomethingException e) {
throw new MyCustomRuntimeException(e);
}
} finally {
return pool.returnObject(objectFromPool); // step (3)
}
(按照您的提及编辑,您希望始终pool.returnObject(objectFromPool);
。
另一种方法是为每个单独的try-catch块而不是这个总体try-finally块创建一个finally块。
答案 4 :(得分:0)
您无法更改所调用方法的代码,并且它们都可以抛出普通Exception
。好的新功能是你可以通过堆栈跟踪来区分哪种方法抛出了异常:
MyObject objectFromPool = null;
try {
objectFromPool = pool.borrowObject();
objectFromPool.doSomething();
} catch (FailedToDoSomethingException e) {
throw new MyCustomRuntimeException(e);
} catch (Exception e) {
//here we get the method name from the first element in the stack trace
//this way we can identify the method that actually threw our exception
if(e.getStackTrace()[0].getMethodName().equals("borrowObject")) {
//pool.borrowObject threw the exception
} else if (e.getStackTrace()[0].getMethodName().equals("doSomething")) {
//objectFromPool.doSomething() threw the exception
}
} finally {
if (objectFromPool != null) {
pool.returnObject(objectFromPool);
}
}
您甚至可以通过比较堆栈跟踪中的getClassName()
来扩展它。
请记住,在重构的情况下,这可能会出错。如果您更改其中一个方法的名称,if
子句可能会失败,以防您忘记在此更改名称。