是“抛出可扔”的好习惯

时间:2012-09-24 11:45:21

标签: java

过去我会用以下方法阅读大量代码:

public Object doSomething() throws Throwable {
    ...
}

通常的做法是这样做吗?

什么是专业人士缺点

throws Trowable在我看来就像“代理橙”的方式来完成例外事项

编辑


  1. 处理方法中的预期异常

  2. 抛出意外的异常(逐个)

  3. 不关心错误

  4. 这是要走的路吗?

9 个答案:

答案 0 :(得分:45)

你不应该抛出Throwable。这就是原因。

Throwable是可以抛出的事物层次结构的顶层,由ExceptionsErrors组成。由于Errors定义来自不可挽回的条件,因此将它们包含在方法声明中是没有意义的。这只留下Exception

您应该使用throws Exception声明您的方法。


请注意,throws的范围越窄越好。

如果您的方法没有生成异常,则声明您的方法为throws Exception即可,但是调用其他声明为throws Exception的代码,并且您希望例外以渗透呼叫叠加。

如果你的方法是生成异常,那么声明一个更窄的范围,例如throws IOException, MyProcessingException

答案 1 :(得分:8)

这是一个有问题的问题。这与异常处理无关,因为它与代码可读性有关。

这取决于您从哪里获取代码示例。抛出一种方法时,专业人员更喜欢更具体。主要原因是它使您的API更具可读性。例如,如果你的方法抛出Throwable,那基本上意味着任何事情都可能发生,你的方法也不想处理它,无论如何。但实际上,只有少数事情可能发生:

  • 无论您在方法中进行的其他调用所导致的检查异常是什么
  • 根据您自己的断言,无论您检查的是什么异常
  • 无论你没有计划的未经检查的例外
  • 对JVM和环境更全面的错误(java.lang.Error

通过明确说明您要抛出的异常,您告诉API的用户他们应该注意什么。例如,当您使用InputStream时,您会注意到大多数方法至少抛出java.io.IOException,这会为您提供有关您应该注意的内容的有用信息。

编码时,作为一般规则,您希望尽可能保持API的表现力。你基本上有一行代码来显示一个方法的公共API(即它的签名,我猜的注释),所以你想要它完全表达(返回类型,名称,参数,还有抛出的异常)。 / p>

至于捕获throwable并打印堆栈跟踪,我会说除非你可以对它做些什么,否则你不应该捕获异常。相反,让它汇总调用堆栈,直到某个类捕获它来执行某些操作。有时候,它可能会一直滚动到主类,我想必须抓住它并打印堆栈跟踪作为最后的手段。基本上,如果你不能对异常采取行动,那么让它上升到调用堆栈。此外,你发现自己处于一种你应该使异常沉默的情况(即抓住它但却什么也不做)是非常罕见的。在解决问题时,通常会引发问题。

对于一般滥用异常处理,这是一个有趣但有趣的article

答案 2 :(得分:1)

从功能上讲,它与throws Exception等效,因为错误未经检查。

我认为没有理由声明抛出Throwable的方法。但是,这并不意味着catch和printStackTrace是一个不错的选择。

通常情况下,你想捕捉扔掉的东西,你可以用它们做一些合理的事情。

抛出一个你不期望的抛出的代码应该光荣地爆炸,所以你可以看到错误并修复错误。

答案 3 :(得分:1)

  

通常的做法是这样做吗?

在JDK中很少见。当不清楚如何处理已检查的异常时,主要使用它。

  

什么是专业人士缺点

优点是您可以编译代码而无需担心已检查的异常。

缺点是您应该处理的异常被忽略。

  

捕获和printStackTrace()不是更好吗?

无论如何通常会打印未处理的异常,因此抓住它们并没有多大帮助。

如果可以通过这样做添加一些值,则应该捕获异常,如果不能,则应该将异常添加到throws子句中。

答案 4 :(得分:1)

这是值得商榷的事情。 让方法抛出太多异常将导致大量错误处理代码。有时候它并不打算。

但是因为我不喜欢签名中的太多异常并不意味着让Lets使用所有异常的Parent而我们已经完成了!!它不起作用。

可以做的是对例如BusinessExceptionServiceException等异常进行分类,这样如果您的业务规则指出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用户想要检测异常特定错误时,他就可以做到,否则可以进行通用错误处理。

此处的核心要点是您应向用户显示的您的余额已经用完而且您无法取款

你可以说在更大的方面显示人类可读的错误形式,确实需要分离异常。

答案 5 :(得分:1)

在极少数情况下,抛出Throwable是可以接受的。例如,Spring AOP中的@Around建议通常被声明为抛出Throwable

以下示例从Spring AOP docs逐字复制:

  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?因为原始方法(即执行连接点)可能会抛出ErrorRuntimeException或已检查的异常。因此,声明doBasicProfiling抛出Throwable只会有意义。

答案 6 :(得分:0)

你是否特别询问Throwable?如果是这样,那么这不是好习惯。它不向类(方法)用户提供任何有用的信息。

答案 7 :(得分:0)

投掷(和捕获)Throwable(或Exception)通常是不好的做法,因为它“覆盖”您可能想要捕获的任何特定异常。然后你将不得不诉诸下面的丑陋:

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还专门为此提供exception hierarchy

通过包装异常,您可以隐藏实现细节,使您的服务层更易于使用。

如果您决定从方法中抛出异常,则需要在callstack中“更高”处理它。这可能是Web应用程序中的一般错误页面,指出出现问题并可能提供错误消息或代码。在某些情况下,更高级别的代码可以尝试重试,或者可能是获得所需结果的替代方法。

答案 8 :(得分:0)

我能想到的唯一用例是单元测试之类的测试代码。但是亚当的反对意见仍然是:“如果这样,那就不是好习惯。它不会为类(方法)用户提供任何有用的信息。”