如何处理一个方法抛给另一个方法的Java中的异常?
How to handle an exception in Java thrown by a method into another method?
假设我有这个 class:
public class Obj1{
...
public void do_Something(int someParameter) throws SomeException {
if(...) throw new SomeException();
...
}
...
}
然后,某处
public class Obj2{
...
public void do_SomeOtherThing(Obj1 obj1){
obj1.do_Something();
//apparently the only solution is try-catching it directly, even if I'm not in the main...
...
}
我了解到异常只能由METHOD抛出,由MAIN捕获,所以,我的问题是:try-catch是处理子方法异常的唯一方法,还是最外部的方法(do_SomeOtherThing) 会抛出,这样我就可以直接在main中try-catch了,删除Object2中的try-catch class?
基本上,我可以做如下吗?
public static void main(String[] args){
Object1 obj1 = new Object1();
Object2 obj2 = new Object2();
try{
obj2.do_SomeOtherThing(obj1);
}
catch(SomeException e){
...
}
}
要不要?
已检查的异常是方法与其调用者之间的契约的一部分,抛出的异常总是需要以这种或那种方式处理。
正确答案取决于具体情况:
- 调用者可以处理的异常:
String getStringFromRemoteServer() throws IOException { ... }
String getConfigString() {
try {
return getStringFromRemoteServer();
} catch (IOException e) {
LOG.warn("Failed to contact server, using local version.", e);
return getLocalString();
}
}
在这种情况下,我们有所需数据的替代来源,因此如果首选方法失败,我们会捕获异常,记录它(以便我们知道我们的网络存在问题)并调用替代方法。
- 异常是致命的,我们不希望调用树中更高层的任何函数尝试处理它。
Configuration parseConfiguration(String configString) throws ParseException { ... }
void loadConfiguration() {
try {
this.globalConfig = parseConfiguration(getConfigString());
} catch (ParseException e) {
throw new RuntimeException("Corrupted config", e);
}
}
在这种情况下,异常意味着我们应用程序的配置已严重损坏。试图处理这个错误是没有意义的,我们的任何调用者试图处理它也没有意义,所以在 loadConfiguration()
上声明 throws
只会造成混乱。我们将异常包装在 RuntimeException
中并重新抛出它。请注意,我们不记录它——会有一些未捕获异常的顶级报告,所以在这里记录它会重复。
让 parseConfiguration()
抛出检查异常仍然很有价值,因为当我们从交互式配置编辑器调用它时,我们会捕获异常并向用户显示错误消息。
- 也许我们的调用者可以处理异常。
int stringToInteger(String s) throws BadNumberException { ... }
String decimalStringToHexString(String s) throws BadNumberException {
return intToHex(stringToInteger(s));
}
在这种情况下,我们没有改变异常的含义——decimalStringToHexString
正在从字符串转换数字,一种可能的结果是该字符串是非法的。我们的调用者需要意识到这是一个可能的结果,就像 stringToInteger()
的调用者一样,所以我们只需声明异常并让我们的调用者处理它。我们的调用者知道他们在其中使用数字的上下文,因此他们可以决定如何处理异常。
一些规则:
- 永远不要完全忽略异常(好的,也许是 InterruptedException)。如果你写
try { ... } catch (Exception e) {}
空的 catch 子句将很难发现你的代码为什么不起作用。
- 包装异常时,始终将原始异常作为原因。
假设我有这个 class:
public class Obj1{
...
public void do_Something(int someParameter) throws SomeException {
if(...) throw new SomeException();
...
}
...
}
然后,某处
public class Obj2{
...
public void do_SomeOtherThing(Obj1 obj1){
obj1.do_Something();
//apparently the only solution is try-catching it directly, even if I'm not in the main...
...
}
我了解到异常只能由METHOD抛出,由MAIN捕获,所以,我的问题是:try-catch是处理子方法异常的唯一方法,还是最外部的方法(do_SomeOtherThing) 会抛出,这样我就可以直接在main中try-catch了,删除Object2中的try-catch class?
基本上,我可以做如下吗?
public static void main(String[] args){
Object1 obj1 = new Object1();
Object2 obj2 = new Object2();
try{
obj2.do_SomeOtherThing(obj1);
}
catch(SomeException e){
...
}
}
要不要?
已检查的异常是方法与其调用者之间的契约的一部分,抛出的异常总是需要以这种或那种方式处理。
正确答案取决于具体情况:
- 调用者可以处理的异常:
String getStringFromRemoteServer() throws IOException { ... }
String getConfigString() {
try {
return getStringFromRemoteServer();
} catch (IOException e) {
LOG.warn("Failed to contact server, using local version.", e);
return getLocalString();
}
}
在这种情况下,我们有所需数据的替代来源,因此如果首选方法失败,我们会捕获异常,记录它(以便我们知道我们的网络存在问题)并调用替代方法。
- 异常是致命的,我们不希望调用树中更高层的任何函数尝试处理它。
Configuration parseConfiguration(String configString) throws ParseException { ... }
void loadConfiguration() {
try {
this.globalConfig = parseConfiguration(getConfigString());
} catch (ParseException e) {
throw new RuntimeException("Corrupted config", e);
}
}
在这种情况下,异常意味着我们应用程序的配置已严重损坏。试图处理这个错误是没有意义的,我们的任何调用者试图处理它也没有意义,所以在 loadConfiguration()
上声明 throws
只会造成混乱。我们将异常包装在 RuntimeException
中并重新抛出它。请注意,我们不记录它——会有一些未捕获异常的顶级报告,所以在这里记录它会重复。
让 parseConfiguration()
抛出检查异常仍然很有价值,因为当我们从交互式配置编辑器调用它时,我们会捕获异常并向用户显示错误消息。
- 也许我们的调用者可以处理异常。
int stringToInteger(String s) throws BadNumberException { ... }
String decimalStringToHexString(String s) throws BadNumberException {
return intToHex(stringToInteger(s));
}
在这种情况下,我们没有改变异常的含义——decimalStringToHexString
正在从字符串转换数字,一种可能的结果是该字符串是非法的。我们的调用者需要意识到这是一个可能的结果,就像 stringToInteger()
的调用者一样,所以我们只需声明异常并让我们的调用者处理它。我们的调用者知道他们在其中使用数字的上下文,因此他们可以决定如何处理异常。
一些规则:
- 永远不要完全忽略异常(好的,也许是 InterruptedException)。如果你写
try { ... } catch (Exception e) {}
空的 catch 子句将很难发现你的代码为什么不起作用。 - 包装异常时,始终将原始异常作为原因。