什么时候应该抛出运行时异常?

38

最近,我参加了一家公司的面试,他们给了我一个编程问题。这个问题与纸牌堆有关,其中一个方法是洗牌。因此,我写出了以下程序:

/** Shuffle the list of cards so that they are in random order 
 * @param d Deck of cards*/
public  static void shuffle(Deck d)
{
    if(d == null)
        throw new IllegalArgumentException();
    Random randomGenerator = new Random();
    List<Card> cards = d.getDeckOfCards();   // cards is basically Linked List.. cards = new LinkedList<Cards>()
    for(int i=0;i<cards.size();i++)
    {
        int randomNumber = randomGenerator.nextInt(52);
        Card c1 = cards.remove(randomNumber);
        Card c2 = cards.remove(0);
        cards.add(0, c1);
        cards.add(randomNumber,c2);
    }       

}
在上述代码中,我抛出了IllegalArgumentException,这是我最怀疑的。在什么情况下应该抛出运行时异常?我们是否应该抛出运行时异常?
谢谢

在上述代码中,我抛出了一个IllegalArgumentException异常,这是我最怀疑的部分。你是指throw new IllegalArgumentException();吗? - motoku
4
在这里通过抛出“IllegalArgumentException”来实现“尽早失败”的做法,我认为没有任何问题,特别是因为传递“null”是无效的。不过,我可能会考虑使用带有“String”的版本来描述出错原因。如果“shuffle”是Deck类的一个方法,就不需要检查空值了。 - Matt Coubrough
javadoc中得知:IllegalArgumentException: 抛出以指示方法已传递非法或不适当的参数 - Matt Coubrough
3
通常情况下,我们会针对这种情况抛出“NullPointerException”异常。 - Radiodef
1
IllegalArgumentException 实际上是一个 RuntimeException - Stefan Falk
上面的代码实际上可以通过省略对d == null的检查来改进,因为如果d为null,那么d.getDeckOfCards()会自动抛出NullPointer。添加一个javadoc注释 '@throws NullPointerException if d is null',一切都很好。 - RHa
3个回答

53

我们应该抛出运行时异常吗?

是的,我们应该抛出运行时异常。运行时异常有特定的目的-它们表示只能通过更改代码而不是更改程序运行环境来解决的编程问题。

在什么情况下应该抛出运行时异常?

当检测到类或方法使用错误时,应抛出运行时异常。

通常情况下,需要抛出运行时异常的情况可分为两类:

  • 传递无效参数值 - 这是运行时异常最常见的原因。大多数参数验证异常都应该是运行时异常。Java提供了几个子类来表示这些特定问题。
  • 以错误的顺序调用方法 - 这是另一种常见原因。当某些方法在类完成初始化或其他准备步骤之前不能被调用时,错误的时间调用应该引起运行时异常。

从这个意义上说,你的代码很好:它完全符合第一类,即传递无效参数值。我会稍微有所不同地添加一条消息,说明哪个参数具有无效值,但在你的情况下,这并不重要,因为只有一个参数。


4
很抱歉,这个答案没有解释为什么在这两种情况下应该抛出运行时异常。抛出它们能够实现什么目的呢? 我也对区分可查和运行时异常的解释感到困惑:可查异常表示需要“改变程序运行的环境”......我想知道作者的意思——什么是‘环境’? - Andrey M. Stepanov
嗨@AndreyM.Stepanov。在这两种情况下应该抛出RuntimeException,因为它可以防止调用堆栈的其余部分必须声明异常。从Javadocs中可以看到:“RuntimeException及其子类是未经检查的异常。如果方法或构造函数的执行可能引发它们并传播到方法或构造函数边界之外,则不需要在方法或构造函数的throws子句中声明未经检查的异常。” - JohnIV
我们应该在抛出运行时异常之前记录日志。由于从方法中抛出的运行时异常没有必要捕获,因此不能保证这些异常会被记录。 - Viraj
1
通常在最后机会的异常处理程序中记录运行时异常,例如在main方法内部。 - Sergey Kalinichenko

17

4
例如,如果您正在阅读文件,并且发生了一些IO错误,则不太可能从错误中恢复,因此将错误重新抛出到顶部,从而终止应用程序并不是一个坏的做法。
另一方面,如果您预期可以恢复错误,则绝对应该捕获和处理错误。例如,您可能有用户在表单中输入数据。如果他们输入不正确的数据,则您的输入处理代码可能会抛出异常(例如,在解析格式不正确的数字字符串时会抛出NumberFormatException)。您的代码应该捕获这些异常并向用户返回错误,提示正确的输入。
当捕获异常并抛出RuntimeException时,重要的是将原始异常设置为RuntimeException的原因,即:
throw new RuntimeException(originalException)。

1
你能否澄清最终的想法:为什么你认为将“throw new RuntimeException(originalException)”视为一个好主意? - Andrey M. Stepanov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接