316

运行时:

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?

(实际上,我不知道这是否是“向上转型”,因为我抛出了 null)

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

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

4

7 回答 7

425

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

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

JLS 14.18指定了这种行为:

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

于 2013-07-10T17:18:12.517 回答
97

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

根据JLS 14.18

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

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

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

这就是尼尔·加夫特所说的(存档

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

于 2013-07-10T17:23:49.990 回答
21

它的行为符合JLS

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

于 2013-07-10T17:22:56.263 回答
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);
}
于 2013-07-10T22:19:14.990 回答
13

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

于 2013-07-10T17:18:30.733 回答
-3

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

JLS 文档关于 throw as :

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

于 2013-07-11T12:54:22.180 回答
-3

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

*不包括原始类型。

于 2013-07-12T15:12:29.023 回答