在过去,我会使用以下方法阅读大量代码:
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
对 JVM 和环境更具有全局性的错误 ( )通过明确说明您要抛出的异常,您是在告诉 API 的用户他们应该注意什么。例如,当您使用 时InputStream
,您会注意到大多数方法 throw 至少java.io.IOException
,这为您提供了一些关于您应该注意什么的有用信息。
在编码时,作为一般规则,您希望尽可能保持 API 的表现力。你基本上有一行代码来显示一个方法的公共 API(即它的签名,我猜也是注释),所以你希望它完全具有表现力(返回类型、名称、参数,还有抛出的异常)。
至于捕获可抛出对象并打印堆栈跟踪,我会说您不应该捕获异常,除非您可以对此做些什么。相反,让它滚动调用堆栈,直到某个类抓住它来做一些事情。有时,它可能会一直滚动到您的主类,我想它必须抓住它并打印堆栈跟踪作为最后的手段。基本上,如果你不能对异常采取行动,那就让它进入调用堆栈。此外,您极少会发现自己处于应该使异常保持沉默的情况(即捕获它但不采取任何措施)。当需要解决问题时,这通常会引发问题。
这是一篇关于滥用异常处理的有趣但有趣的文章。
在极少数情况下,抛出Throwable
s 是可以接受的。例如,@Around
Spring AOP 中的通知通常被声明为抛出一个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
.
在功能上,它等同于throws Exception
,因为错误是未经检查的。
我认为没有理由声明抛出 Throwable 的方法。然而,这并不意味着 catch 和 printStackTrace 是一个好的选择。
通常,您希望捕获可以对它们做一些明智的事情的可投掷物。
抛出你没想到的 throwable 的代码应该会爆炸,所以你可以看到错误并修复错误。
这样做是常见的做法吗?
在 JDK 中很少见。这主要用于不清楚如何处理已检查的异常时。
有什么优点和缺点?
优点是你可以编译你的代码而不用担心检查异常。
缺点是您应该处理的异常被忽略了。
catch 和 printStackTrace() 不是更好吗?
无论如何,通常都会打印未处理的异常,因此捕获它们并没有多大帮助。
当您可以通过这样做添加一些值时,您应该捕获异常,并在不能throws
时将异常添加到子句中。
这真是一个值得商榷的事情。让方法抛出太多异常将导致大量错误处理代码。有时它不是故意的。
但是因为我不喜欢签名中的太多异常并不意味着让我们使用所有异常的父级并且我们完成了!不起作用。
可以 做的是对异常进行分类,BusinessException
例如ServiceException
minimum balance in account can not be less than say 100$
InsufficientBalance
BusinessException
所以你的方法会像
public Object doSomething() throws BusinessException {
if(!hasMinimumbalance())
{
throw new InsufficientBalance(ErrorCode);
}
}
这将做的是将相关的异常组合在一起,并且每当 API 用户想要检测异常特定的错误时,他就可以做到,否则通用错误处理是可能的。
这里的核心点是在你应该向用户显示你已经用完余额并且你不能提款的 UI 上
您可以在更大的方面说,为了显示人类可读的错误形式,确实有必要分离异常。
你是在问 Throwable 吗?如果是这样,那么这不是一个好习惯。它不向类(方法)用户提供任何有用的信息。
抛出(和捕获)Throwable(或异常)通常是不好的做法,因为它“覆盖”了您可能想要捕获的任何特定异常。那么你将不得不诉诸丑陋,如下所示:
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)来报告异常。即便如此,仅仅记录它可能还不够。
我的经验法则是:
如果您可以在本地处理异常,请执行此操作。例如,当将 String 解析为 Integer 时,您可以捕获 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 应用程序中的通用错误页面,说明出现问题并可能提供错误消息或代码。在某些情况下,更高级别的代码可以尝试重试或可能的替代方式来获得所需的结果。
我能想到的唯一用例是单元测试之类的测试代码。但亚当的对立面仍然是“如果是这样,那么这不是一个好的做法。它没有为类(方法)用户提供任何有用的信息。”