【问题标题】:If you explicitly throw an unchecked exception in your code, isn't it now a checked exception - because the compiler can now check if it's handled?如果您在代码中显式抛出未经检查的异常,那么它现在不是已检查的异常吗 - 因为编译器现在可以检查它是否已被处理?
【发布时间】:2021-07-25 00:31:17
【问题描述】:

在 Cay Horstmann 的 Java 9 书中,它说

Integer.parseInt(String str);

“它会抛出未经检查的 NumberFormatException”。

NumberFormatException 继承自 RuntimeException。 当我查看 Java 11 文档时,它是在签名中声明的。 所以调用方法必须在某个时候处理它。

public static int parseInt(String s) throws NumberFormatException

现在不是已检查异常 - 因为编译器可以检查其使用情况吗?

更新:编译器是否检查NumberFormatException 是否由调用者处理?我看不出编译器从不需要调用方法(甚至那些远离调用方法的方法)来捕获检查的异常有什么好处。

【问题讨论】:

  • 没有。 throws 子句完全是为了方便程序员阅读文档。
  • 这是一个 declared 异常 - 不会使其成为 checked 异常。编译器不会检查 NumberFormatException 仅从该方法或其他声明它的方法中抛出。
  • @kaya3 "编译器不会检查 NumberFormatException 是否仅从此方法或其他声明它的方法中抛出。"嗯?我不明白。为了编译代码,编译器必须检查是否抛出了异常。
  • 不,完全允许声明一个实际上没有抛出的异常,例如void foo() throws Exception { /* do nothing */ }。 “检查的异常”并不意味着编译器检查每个方法可以抛出它声明的异常,它意味着编译器检查该类型的异常只能被抛出通过声明它的方法。
  • @kaya3 编译器是否检查 NumberFormatException 是否由调用者处理?

标签: java


【解决方案1】:

...现在不是已检查异常吗-因为编译器可以检查其使用情况?

没有。

JLS 11.1.1 这么说:

已检查异常 类是除未检查异常 类之外的所有异常类。也就是说,已检查异常类是Throwable 及其所有子类,而不是RuntimeException 及其子类和Error 及其子类。

这就是定义。

您可以在throws 子句中声明任何异常,但编译器只会检查已检查的异常,以确保它们在调用方法中得到处理或声明。


所以调用方法必须在某个时候处理它。

不正确。

unchecked 异常的throws 不会改变编译器处理代码的方式,也不会改变任何运行时行为。 throws 不会检查异常。它不会强制调用方法处理异常。

throws 子句中声明未经检查的异常的(表面上的)原因是记录该方法可能引发异常的事实。这将转化为方法的 javadocs 中的文本,程序员可以(应该)阅读以了解该方法以及如何使用它。


在您的示例中,NumberFormatExceptionRuntimeException 的子类,因此它不是已检查异常;见javadoc。但是,程序员仍然需要知道parseInt 可以抛出(未经检查的!)NumberFormatException,因此他们无论如何都将throws NumberFormatException 包含在parseInt 声明中。


好的,那么您建议的语义会产生什么影响?

这意味着根据方法的throws 子句检查或取消检查异常。因此,一种方法可能会声明 NumberFormatException 已检查,而另一种方法可能会声明未检查。

我认为编译器可以解决这个问题。但我不确定一个典型的程序员可以做到。例如,它使决定在方法的throws 子句中放入什么变得更加困难。

第二件事是,这将允许程序员通过不使用任何throws 子句来有效地忽略检查异常。但最初的 Java 基本原理是,最好强制程序员处理异常……因为这会导致代码更加健壮。 Java 检查异常(如当前指定的)在编译时消除了一类重要的程序员错误。

为了避免第二个问题,您需要一些额外的语法,允许程序员明确地将异常标记为未检查。例如

public void method1() throws AnException, AnotherException { 
     ...
}

public void method2() {
    try {
        method1();
    } catch (AnException e) {
    } unchecked AnotherException
}

unchecked 子句是否意味着AnotherException 不再被视为已检查...并且不需要method2 声明。

(但我可以想象许多程序员滥用 unchecked 构造,如果它是由语言提供的。)

【讨论】:

  • 你是说编译器不检查它是否被处理(在我的例子中)?我不建议更改语言。对我来说,编译器不检查它是否已被处理,因为它现在已声明,这对我来说没有意义。
  • 编译器不会检查NumberFormatException 是否被处理,因为NumberFormatException 不是检查异常。如果您查看javadoc,您会看到NumberFormatException 具有RuntimeException 作为超类。
  • 您说,“编译器不会检查 NumberFormatException 是否被处理”。这与@rzwitserloot 下面的答案相矛盾。谁是对的?
  • 我认为他可能在他的 cmets 中说错了。 (他将“在throws 子句中声明”与处理异常混为一谈。)但这不相关,因为JLS 无论如何都没有用这些术语表达规则。如果您真的想了解这些东西,请阅读 JLS ...并非常仔细地阅读。从阅读和理解Chapter 11全部开始
  • 为了记录,throws 子句与抛出的异常之间的关系的非规范性描述如下: “本质上,对于每个检查的异常,可能是由于执行的主体方法或构造函数,除非其异常类型或其异常类型的超类型在方法或构造函数的声明中的 throws 子句中提及,否则会发生编译时错误。”。它没有使用“处理”一词。所以挑剔“处理”是什么意思是无关紧要的。
【解决方案2】:

本来可以,但语言根本不是那样设计的。

有几件事就是这样。

  • 所有源文件都秘密地包含import java.lang.*;。不管你写不写。编译器的行为就像它在那里一样。

  • 所有方法和构造函数的行为就好像它们上面有throws RuntimeException, Error;。你不能让一个方法没有这个。无论您下令什么,都只是添加到那堆中。因此,任何调用已知抛出 NumberFormatException 的方法的方法都不需要处理它,因为它已声明它会向前抛出它(因为 all 方法被定义为 throws RuntimeException即使你没有写,NFEx 是 RuntimeException 的子类型。

对异常的一个有趣的替代方案可能是,任何显式声明的内容都会因此被检查,而任何未声明的内容都不会被检查,并且 java 会生成一个新的 throw 指令,该指令不需要您也声明该类型在你的throws 块中。

但是,java 不是这样工作的,据我所知,没有任何语言可以。

NB:那么为什么有些方法显式声明为抛出一些未经检查的异常类型,为什么有些源文件包含import java.lang.*;?因为该文件的作者认为它有助于提高可读性。那个 import 语句很难押韵,实际上你很少看到。但是明确声明你抛出一个未经检查的类型有点常见。他们这样做的原因是完全为了可读性。它什么也不做。这是一条不在/* */ 中的评论。评论也完全没有任何作用,但人们确实会写。

【讨论】:

  • 但是既然现在声明了,为什么编译器不能检查它是否被处理?
  • @likejudo 重读答案。它正在检查它是否被处理,答案总是肯定的:它被处理是因为方法有throws RuntimeException。即使您看到某种计划从根本上改变 Java 的工作方式,这也意味着大量现有代码现在不再工作,而 Java 不会做那种事情。
  • 你说,“它正在检查它是否被处理,答案总是肯定的:”。这与@Stephen C 的答案相矛盾。你们俩的分数都很高——但只有一个答案是正确的。哪一个?
  • 一个方法只需要声明它抛出检查的异常,编译器永远不会检查它是否在任何地方被捕获? 正确。当然,该方法是无用的,除非有人调用它,然后 他们 必须捕获它,或者在其 throws 子句中声明它。直到您到达“入口点”,例如public static void main,该过程才结束。哪个可以throws Exception声明,如果你愿意(你通常应该——main可以抛出它喜欢的任何东西。它是需要管理它们的异常的库方法)。
  • 注意:如果您没有看到重点,请尝试一些或多想一些。很明显:所有已检查的异常都需要在某个地方捕获,您只是被允许(通过throws 子句)延迟处理它的时间点,只要您想这样做,但您必须声明这一点明确地,这将成为您 API 的一部分。
猜你喜欢
  • 1970-01-01
  • 2023-04-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-16
  • 1970-01-01
相关资源
最近更新 更多