过去我读了很多像这样的方法代码:
public Object doSomething() throws Throwable {
...
}
这是常见做法吗?
有哪些利弊呢?
throws Trowable
对我来说像是“橙剂”式的处理异常方式。
编辑
在方法中处理预期的异常
一次抛出一个未预期的异常
不关心错误
这样做是否可行?
过去我读了很多像这样的方法代码:
public Object doSomething() throws Throwable {
...
}
这是常见做法吗?
有哪些利弊呢?
throws Trowable
对我来说像是“橙剂”式的处理异常方式。
编辑
在方法中处理预期的异常
一次抛出一个未预期的异常
不关心错误
这样做是否可行?
不应该抛出 Throwable
。原因如下。
Throwable
是可以抛出的东西的层次结构的顶部,由 Exceptions
和 Errors
组成。由于 Errors
根据定义起源于无法挽救的情况,因此在方法声明中包含它们是没有意义的。这样就只剩下了 Exception
。
你应该使用 throws Exception
来声明你的方法。
请注意, throws
的范围越小越好。
如果你的方法不生成异常,而是调用其他被声明为 throws Exception
的代码,并且你希望异常在调用堆栈上传播,则将方法声明为 throws Exception
是可以接受的。
如果你的方法是产生异常的,则声明一个更窄的范围,例如 throws IOException,MyProcessingException
等。
这是一个很有深度的问题。这并不只涉及异常处理,还关乎代码的可读性。
这取决于你从哪里获得代码示例。专业人士更倾向于在方法中抛出更具体的异常。主要原因是这样可以使您的API更加易读。例如,如果您的方法抛出Throwable,那基本上意味着任何事情都可能发生,并且您的方法不想处理它。但实际上,只有有限的一些事情可能会发生:
java.lang.Error
)通过具体说明您想要抛出的异常,可以告诉API使用者应该注意什么。例如,当您使用InputStream
时,您会注意到大多数方法都至少会抛出 java.io.IOException
,这为您提供了一些有用的信息,让您知道应该注意什么。
编码时,通常情况下,您希望尽可能使API表达力更强。您只有一行代码来展示方法的公共API(即其签名,以及注释),因此您希望它完全表达清楚(返回类型、名称、参数,但也包括抛出的异常)。
至于捕获可抛出对象并打印堆栈跟踪,我建议您不要捕获异常,除非您可以对其进行处理。相反,让它沿着调用堆栈向上滚动,直到某个类捕获它并处理它。有时,它可能会一直升级到您的主类,最终必须捕获并打印堆栈跟踪。基本上,如果您无法对异常进行处理,请让它沿着调用堆栈向上滚动。此外,很少有情况需要您将异常静默处理(即捕获异常但不做任何处理)。这通常会在解决问题时招致麻烦。
这是一篇有趣但有关通用异常处理滥用的文章。
在某些罕见情况下,抛出 Throwable
是可以接受的。例如,在 Spring AOP 中声明的 @Around
通知通常会抛出一个 Throwable
。
以下示例摘自Spring AOP文档,原文未作任何修改:
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.ProceedingJoinPoint;
@Aspect
public class AroundExample {
@Around("com.xyz.myapp.SystemArchitecture.businessService()")
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
// start stopwatch
Object retVal = pjp.proceed();
// stop stopwatch
return retVal;
}
}
为什么声明 doBasicProfiling
抛出 Throwable
? 因为原方法(即执行连接点)可能会抛出 Error
、RuntimeException
或已检查的异常。 所以,将 doBasicProfiling
声明为抛出 Throwable
是有意义的。
这是一个有争议的问题。如果方法抛出太多异常,将导致大量的错误处理代码。有时这并不是我们想要的。
但是,因为我不喜欢在签名中使用太多异常,并不意味着让我们使用所有异常的父类就可以了!这样做是行不通的。
我们可以对异常进行分类,例如BusinessException
、ServiceException
,这样如果您有一个业务规则,规定账户的最低余额不能少于100美元,则会生成InsufficientBalance
异常,它将是BusinessException
的子类。
因此,您的方法将会是这样的:
public Object doSomething() throws BusinessException {
if(!hasMinimumbalance())
{
throw new InsufficientBalance(ErrorCode);
}
}
这样做的作用是将相关的异常组合在一起,每当API用户想要检测特定的异常错误时,他就可以这样做,否则可以进行通用错误处理。
这里的核心点是在UI上向用户显示您已经用完了余额,无法提取资金
从更大的方面来说,为了显示可读性强的错误形式,有必要对异常进行分离。
从功能上讲,它与throws Exception
相当,因为错误是未经检查的。
我认为没有理由声明一个方法抛出Throwable。然而,这并不意味着catch和printStackTrace是一个好的替代方案。
通常情况下,您希望在可以对其进行有意义处理的地方捕获throwables。
抛出您不期望的throwable的代码应该爆炸性地失败,以便您可以看到错误并修复错误。
Throwable
s。 - Judge Mentalthrows
子句中,当您无法添加异常时。public void myMethod() throws Throwable {
if (x) {
throw new MyException1();
}
if (y) {
throw new MyException2();
}
}
public void callingMethod() {
try {
myMethod();
}
catch(Throwable t) {
if (t instanceof MyException1) {
// handle exception 1
}
else if (t instanceof MyException2) {
// handle exception 2
}
else {
// handle other exceptions
}
}
}
这种写法容易出错(并被CheckStyle标记为代码违规)。最好的做法是像这样编写代码:
public void myMethod() throws MyException1, MyException2 {
if (x) {
throw new MyException1();
}
if (y) {
throw new MyException2();
}
}
public void callingMethod() {
try {
myMethod();
}
catch(MyException1 e) {
// handle exception 1
}
catch(MyException2 e) {
// handle exception 2
}
}
仅通过调用printStackTrace()来处理异常通常不是一个好主意。printStackTrace()将堆栈跟踪发送到标准错误,可能根本不会被读取。更好的选择是使用应用程序的日志记录工具(如log4j)报告异常。即使这样,仅仅记录下来可能还不够。
我的经验法则是:
如果可以在本地处理异常,请这样做。例如,在将字符串解析为整数时,您可以捕获NumberFormatException并返回默认值:
prvate int parseAmount(String amountValue) {
int amount;
try {
amount = Integer.parseInt(amountValue);
}
catch(NumberFormatException e) {
// default amount
amount = 0;
}
return amount;
}
如果您无法在本地处理异常,请考虑是否应该公开抛出的异常类型。如果这种类型是某些模糊的(依赖于实现)类型,则将其包装在自己的通用异常类型中可能是一个好主意:
private Customer getCustomer(int customerId) throws ServiceException {
try {
return customerService.getCustomer(customerId);
}
catch(CustomerServiceSpaghettiTangledException e) {
throw new ServiceException("Error calling the customer service", e);
}
}
这里的'ServiceException'是您创建的Exception子类。Spring还专门为此提供了异常层次结构。
通过包装异常,您隐藏了实现细节,使您的服务层更加简单易用。
如果您决定从方法中抛出异常,则需要在调用堆栈的“更高层”处理它。这可以是Web应用程序中的通用错误页面,指出出现了问题并可能提供错误消息或代码。在某些情况下,更高级别的代码可以尝试重试或可能采用替代方法来获取所需的结果。