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

Posted

技术标签:

【中文标题】为啥我可以在 Java 中抛出 null? [复制]【英文标题】:Why can I throw null in Java? [duplicate]为什么我可以在 Java 中抛出 null? [复制] 【发布时间】:2013-07-08 18:05:10 【问题描述】:

运行时:

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?

(实际上,我不知道这是否是一个“upcast”,因为我正在抛出 null)

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

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

【问题讨论】:

一旦知道发生了什么,如果enull,那么e instanceof XYZ 将是false,不管XYZ 是什么。也就是说,你没有抓到null;您捕获了NullPointerException 的实际实例。至于“为什么会有人throw null——人们可能会无意中这样做(如throw this.lastException();,其中lastException()返回null。)关于“有趣的事实”——我猜IntelliJ 不是那么智能。提交错误报告! "...我的行 e instanceof NullPointerException 将永远是假的。这根本不是真的" - 双关语? 因为 throw 语句可以抛出引用类型 objects..因为对象可以是 null 它允许你抛出 null 值 @Anirudh 不是所有的引用类型,只有 Throwable 类型的引用,因为 Throwable 可以是 null 引用,所以它允许。 另见Why throw null; is not creating compilation error in Java? 【参考方案1】:

为什么将它向上转换为 NullPointerException?

根据JLS 14.18:

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

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

您可以抛出 Throwable 类型的对象,并且由于 nullThrowable 的有效引用,编译器允许这样做。

这就是Neal Gafter says (archived)

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

【讨论】:

这是最好的答案,因为它是唯一解释为什么它不是编译时错误的答案。 (或者至少,唯一一个解释没有错的人!) @ruakh 我真的很喜欢这个答案,但是选择的答案略胜一筹,所选答案的内容几乎回答了我的问题。很想知道为什么 Neal Grafter 认为这是一个编译错误...... @bharal:我认为他的评论很清楚:Java 语言规范,第三版,要求throw 语句中的表达式具有引用类型。 null 文字没有引用类型(它只能分配给引用类型)。 Gafter 打算删除此要求,并相应地修改了javac,但随后要求没有删除,因此javac 的行为是一个错误。 (也就是说,上述更改确实使其成为规范的 Java SE 7 版本,现在 [继续] [继续]requires the expression to be of reference type or null type。因此,在 Java 7 编译器或设置为 Java 7 源代码级别的编译器中,这不再是错误。)【参考方案2】:

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

关于 throw as 的 JLS 文档:

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

【讨论】:

这是 Java,不是 C#。 @bmargulies 你从哪里得到 c#?没有提到。 我不知道为什么我在 2013 年写了这篇文章。 可能是因为它曾经提到过“System.Exception”,这是一个C#概念。【参考方案3】:

null 可以转换为任何东西*,包括异常。就像你可以返回 null 如果你的方法签名指定你应该返回一个异常(或者实际上是一个字符串,或者 Person 类),你可以抛出它。

*不包括原始类型。

【讨论】:

这并不能解释为什么 null 是 NullPointerException 的实例而不是 FileNotFoundException 的实例【参考方案4】:

以这种方式思考它可以更清楚地了解它的工作原理:

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);

【讨论】:

【参考方案5】:

看起来不是null 被视为NullPointerException,而是尝试throw null 的行为本身 抛出NullPointerException

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

JLS 14.18 specifies 这种行为:

如果表达式的评估正常完成,产生一个空值,则创建并抛出类 NullPointerException 的实例 V' 而不是空值。然后 throw 语句突然完成,原因是带有值 V' 的 throw。

【讨论】:

@fvrghl:像 Java 的许多异常处理一样,throw null 没有有效理由,但它可能会出现在一个有缺陷的程序中。为有缺陷的构造定义明确的语义可以简化调试并最大限度地减少错误的安全后果。 @bharal:在编译时可能无法知道。在throw null 的确切情况下,它是,但在更一般的情况下,异常对象是在运行时决定的,可能没有办法知道。显然(在 JLS 中)为 null 文字添加一个特殊情况并不值得,因为无论如何这是一个非常容易避免的错误。 要详细说明@Mankarse 的评论,您可能有一些生成异常的方法,例如Exception generateExceptionForErrorCode( int errorCode ) 或其他什么,如果(由于错误)返回null,您将结束尝试抛出空值。 Java 编译器不进行 null 分析,因此不会注意到这一点。 好吧,throw null 的代码比 throw new NullPointerException() 少,这不是一个好理由,但这是一个原因。 @fvrghl 请记住,您不能只使用throw new SomethingException(),还可以使用throw existingExceptionYouGotFromSomewhere。当您有一个复杂的异常处理程序时,您会这样做,该处理程序本身能够处理一些异常但将其他异常传播到上层。在这种情况下,重新抛出异常的异常处理代码可能存在导致异常对象为null 的错误。【参考方案6】:

它的行为符合JLS:

如果表达式的评估正常完成,产生一个空值,则创建并抛出类 NullPointerException 的实例 V' 而不是空值。

【讨论】:

【参考方案7】:

不确定,但我猜是“抛出 null”; not 是否工作,并且尝试它会导致程序抛出异常,并且该异常恰好是(鼓)NullPointerException...

【讨论】:

以上是关于为啥我可以在 Java 中抛出 null? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

如何在 lambda Java8 中抛出 Checked Exception? [复制]

为啥我不能在 Promise.catch 处理程序中抛出?

为啥我收到错误工厂方法'halLinkDisocoverer'在springboot中抛出异常?

为啥在 ios/cordova 中抛出这个异常?

为啥 nodemon 在终端中抛出一些错误?

sql 在简单查询中抛出错误