为什么我可以在 Java 中抛出 null?[复制]

Why can I throw null in Java? [duplicate]

提问人:bharal 提问时间:7/11/2013 最后编辑:Peter Mortensenbharal 更新时间:6/8/2020 访问量:36656

问:

运行此命令时:

public class WhatTheShoot {

    public static void main(String args[]){
        try {
            throw null;
        } catch (Exception e){
            System.out.println(e instanceof NullPointerException);
            System.out.println(e instanceof FileNotFoundException);
        }
    }
}

响应是:

true  
false

这对我来说相当惊人。我本来以为这会导致编译时错误。

为什么我可以在 Java 中抛出 null,为什么它会将其转换为 NullPointerException?

(实际上,我不知道这是否是“上投”,因为我抛出的是空)

除了一个非常非常愚蠢的面试问题(请不要在面试中问这个问题)之外,我看不出有任何理由.也许你想被解雇,但那是......我的意思是,为什么还有人会?throw nullthrow null

有趣的事实 IntelliJ IDEA 12 告诉我,我的行 , 将永远是假的。这根本不是真的。e instanceof NullPointerException

Java 异常 nullPointerException

评论

17赞 Ted Hopp 7/11/2013
一旦知道发生了什么,如果是,那么无论是什么,都会是。换句话说,你没有抓住;您捕获了 的实际实例。至于“为什么还有人会抛出 null”——人们可能会无意中这样做(比如在 where returns 中)。回复“有趣的事实”——我猜 IntelliJ 不是那么聪明。提交错误报告!enulle instanceof XYZfalseXYZnullNullPointerExceptionthrow this.lastException();lastException()null
74赞 BlueRaja - Danny Pflughoeft 7/11/2013
“......我的行 e instanceof NullPointerException 将始终为 false。这根本不是真的“——双关语?
3赞 Anirudha 7/11/2013
这是因为 throw 语句可以抛出引用类型。由于对象可以为 null,因此它允许您抛出 null 值objects
4赞 AllTooSir 7/11/2013
@Anirudh 不是所有的引用类型,只有 和 类型的引用 可以是引用 ,它允许它。ThrowableThrowablenull
2赞 devnull 7/11/2013
另请参阅为什么抛出 null;在 Java 中没有创建编译错误?

答:

429赞 Louis Wasserman 7/11/2013 #1

看起来它不是被视为 ,而是试图自己抛出 .nullNullPointerExceptionthrow nullNullPointerException

换句话说,检查其参数是否为非空,如果为空,则抛出 .throwNullPointerException

JLS 14.18 指定了以下行为:

如果表达式的计算正常完成,生成 null 值,则创建并抛出类 NullPointerException 的实例 V',而不是 null。然后 throw 语句突然完成,原因是值为 V' 的抛出。

评论

14赞 Mankarse 7/11/2013
@fvrghl:就像 Java 的许多异常处理一样,没有正当理由,但它可能会出现在一个有缺陷的程序中。为 buggy 构造定义明确的语义可以简化 bug 的调试,并最大程度地减少 bug 的安全后果。throw null
17赞 Mankarse 7/11/2013
@bharal:可能无法在编译时知道。在 的确切情况下,它是,但在更一般的情况下,异常对象是在运行时决定的,可能没有办法知道。显然,在JLS中添加文字的特例是不值得的,因为无论如何,这是一个非常容易避免的错误。throw nullnull
6赞 Calum 7/11/2013
为了详细说明@Mankarse的评论,您可能有一些方法来生成异常,例如或其他什么,如果(由于错误)返回,您最终会尝试抛出 null。Java 编译器不执行空分析,因此不会注意到这一点。Exception generateExceptionForErrorCode( int errorCode )null
8赞 edthethird 7/11/2013
好吧,代码比 少,这不是一个好的理由,但这是一个原因。throw nullthrow new NullPointerException()
3赞 Philipp 7/12/2013
@fvrghl 请记住,您不仅可以而且.当你有一个复杂的异常处理程序时,你会这样做,它本身能够处理一些异常,但将其他异常传播到上层。在这种情况下,重新引发异常的异常处理代码可能存在导致异常对象为 的错误。throw new SomethingException()throw existingExceptionYouGotFromSomewherenull
14赞 ajb 7/11/2013 #2

不确定,但我猜是“抛出 null”;不起作用,尝试它会导致程序抛出异常,而该异常恰好是(鼓声)NullPointerException...

21赞 assylias 7/11/2013 #3

它的行为符合 JLS

如果表达式的计算正常完成,生成 null 值,则创建并抛出类 NullPointerException 的实例 V',而不是 null。

97赞 AllTooSir 7/11/2013 #4

为什么它会将其转换为 NullPointerException?

根据 JLS 14.18

throw 语句首先计算 Expression。如果表达式的计算由于某种原因突然完成,则抛出会因此而突然完成。如果表达式的计算正常完成,生成非空值 V,则 throw 语句突然完成,原因是抛出值为 V 的抛出。 如果表达式的计算正常完成,生成 null 值,则创建并抛出类 NullPointerException 的实例 V',而不是 null。然后 throw 语句突然完成,原因是值为 V' 的抛出。

为什么我可以在java中抛出null?

您可以抛出 and 类型的对象,因为是 的有效引用,编译器允许它。ThrowablenullThrowable

这是尼尔·戈夫特(Neal Gafter)所说的存档))

尽管 null 可以分配给每个引用类型,但 null 的类型本身并不是引用类型。我们的意图是从 JLS 的第三版中删除抛出语句中的表达式是引用类型的要求,但该更改实际上从未进入已发布的版本。因此,这是我在 SE 5 中引入的 javac 编译器错误。

评论

4赞 ruakh 7/11/2013
这是最好的答案,因为它是唯一解释为什么它不是编译时错误的答案。(或者至少,是唯一一个解释没有错的人!
0赞 bharal 7/11/2013
@ruakh我真的很喜欢这个答案,但选择的答案比它多了一点,而且选择的答案的内容几乎回答了我的问题。很想听听为什么 Neal Grafter 认为这是一个编译错误......
5赞 ruakh 7/11/2013
@bharal:我认为他的评论很清楚:Java 语言规范,第三版,要求语句中的表达式具有引用类型。文本没有引用类型(它只能分配给引用类型)。Gafter 打算删除此要求,并对其进行相应修改,但随后没有删除该要求,因此 的行为是一个错误。(话虽如此,上述更改确实进入了 Java SE 7 版的规范,现在 [继续]thrownulljavacjavac
5赞 ruakh 7/11/2013
[续] 要求表达式为引用类型或 null 类型。因此,在 Java 7 编译器或设置为 Java 7 源代码级别的编译器中,这不再是一个错误。
18赞 Nick Gotch 7/11/2013 #5

以这种方式思考可以更清楚地了解为什么会这样:

try {
    Exception foo = null;
    if(false) {
        foo = new FileNotFoundException();
    } // Oops, forgot to set foo for the true case..
    throw foo;
} catch (Exception e){
    System.out.println(e instanceof NullPointerException);
    System.out.println(e instanceof FileNotFoundException);
}
-3赞 techhunter 7/11/2013 #6

巴拉尔...它看起来像一个 javac 编译器错误。我认为它是在 SE 5 中引入的。 可以将 Null 分配给任何引用类型。但是,“null 的类型”本身并不是引用类型。程序编译它,因为 null 可以简单地转换为 Exception。 此外,throw 在声明后查找对象引用,并且由于 null 可以用作对象引用,因此它显示结果。

JLS 文档中关于抛出的内容是:

“throw 语句首先计算表达式。如果评估 的表达式由于某种原因突然完成,然后抛出 由于这个原因突然完成。如果对表达式的计算 正常完成,产生非空值 V,然后抛出 语句突然完成,原因是抛出值为 V。 如果表达式的计算正常完成,则生成 null 值,则创建类 NullPointerException 的实例 V' 并抛出而不是 null。然后,throw 语句完成 突然,原因是值为 V' 的投掷。

评论

1赞 bmargulies 7/14/2013
这是 Java,而不是 C#。
0赞 vidstige 7/7/2015
@bmargulies你从哪里得到 C#?没有提到。
4赞 bmargulies 7/7/2015
我不知道我为什么在 2013 年写这篇文章。
2赞 eis 1/4/2016
可能是因为它曾经提到过“System.Exception”,这是一个 C# 概念。
-3赞 Marc 7/12/2013 #7

null 可以强制转换为任何内容*,包括 Exception。就像如果方法签名指定应返回 Exception(或者实际上是字符串或 Person 类)可以返回 null 一样,也可以抛出它。

*不包括基元类型。

评论

1赞 php_coder_3809625 8/24/2016
这并不能解释为什么 null 是 的实例而不是NullPointerExceptionFileNotFoundException