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

bha*_*ral 308 java exception-handling nullpointerexception

运行时:

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

回应是:

true  
false
Run Code Online (Sandbox Code Playgroud)

这对我来说相当惊人.我原本以为这会造成编译时错误.

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

(实际上,我不知道它是否是"向上",因为我扔的是null)

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

有趣的事实 IntelliJ IDEA 12告诉我,我的行e instanceof NullPointerException总是假的.这根本不是真的.

Lou*_*man 411

看起来它不是null被视为一个NullPointerException,而是试图throw null 自己的行为抛出一个NullPointerException.

换句话说,throw检查它的参数是非空的,如果它是null,它会抛出一个NullPointerException.

JLS 14.18 指定了这种行为:

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

  • @bharal:在编译时可能无法知道.在"throw null"的确切情况下,它是,但在更一般的情况下,在运行时决定异常对象,可能无法知道.显然,对于"null"文字的特殊情况的添加(对JLS)并不值得,因为无论如何都要避免这是一个非常容易的错误. (15认同)
  • @fvrghl:像Java的异常处理一样,没有*有效*的理由来"抛出null",但它可能出现在一个有缺陷的程序中.为错误构造定义良好的语义可以简化调试并最大限度地减少错误的安全后果. (13认同)
  • 好吧,`throw null`比`throw new NullPointerException()`更少的代码,这不是一个好理由,但这是一个原因. (8认同)
  • 要详细说明@Mankarse的注释,你可能有一些生成异常的方法,比如`Exception generateExceptionForErrorCode(int errorCode)`或者其他什么,如果那个(由于一个bug)返回`null`,你最终会尝试抛出null.Java编译器不进行空分析,因此不会注意到这一点. (6认同)
  • @fvrghl请记住,你不能只是`抛出新的SomethingException()`而且还要抛出existingExceptionYouGotFromSomewhere`.当你有一个复杂的异常处理程序时,你会这样做,它能够自己处理一些异常但是将其他异常传播到上层.在这种情况下,重新抛出异常的异常处理代码可能会导致异常对象为"null"的错误. (3认同)

NIN*_*OOP 95

为什么它会将它转发为NullPointerException?

根据JLS 14.18:

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

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

您可以抛出类型的对象,Throwable因为它null是一个有效的引用Throwable,编译器允许它.

这就是Neal Gafter所说的

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

  • @bharal:我认为他的评论很清楚:*Java语言规范*,第三版,要求`throw`语句中的表达式具有引用类型.`null`文字没有引用类型(它只能分配给引用类型).Gafter打算删除这个要求,并相应地修改`javac`,但后来没有删除要求*,所以`javac`的行为是一个bug.(也就是说,上述更改确实使其成为规范的Java SE 7版本,现在*[续]* (5认同)
  • *[续]*[要求表达式为引用类型*或null类型*](http://docs.oracle.com/javase/specs/jls/se7/html/jls-14.html#jls-14.18 ).因此,在Java 7编译器或设置为Java 7源代码级别的编译器中,这不再是错误.) (5认同)
  • 这是最好的答案,因为它是唯一一个解释为什么它不是编译时错误的答案.(或者至少,唯一一个解释没错的人!) (3认同)

ass*_*ias 20

它的行为符合JLS:

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


Nic*_*tch 17

以这种方式思考它会使其更加明显:

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);
}
Run Code Online (Sandbox Code Playgroud)


ajb*_*ajb 13

不确定,但我猜"扔掉"; 它工作,并试图导致程序抛出一个异常,并且该异常恰好是(鼓声)NullPointerException异常...