Java中"(Object)null"和"null"有什么区别?

52

请看下面的例子:

class nul
{
  public static void main (String[] args)
  {
    System.out.println (String.valueOf((Object)null));
    System.out.println (String.valueOf(null));
  }
}

第一个println语句输出null,但第二个语句抛出了一个NullPointerException异常。

为什么只有第二个语句会抛出异常?这两个null之间有什么区别?在Java中是否存在真正的null和伪造的null?


1
如果您查看String的Javadoc,答案就在valueOf的两个相关重载中显而易见。 - bmargulies
为什么调用String.valueOf(null)会抛出NullPointerException异常? - tckmn
3个回答

56
第一次调用将调用String.valueOf(Object)方法,因为您已经将null显式转换为Object引用。相反,第二次调用将调用重载的String.valueOf(char[])方法,因为对于null参数,char[]Object更具体。
此方法还有其他重载版本,可以接受原始参数,但对于null参数,这些参数不是有效匹配项。
JLS §15.12.2

可能会有多个这样的方法,如果是这种情况,则选择最具体的方法。在运行时执行方法分派的描述符(签名加返回类型)是使用的最具体的方法。

如果一个方法是适用的,则它是通过子类型适用(§15.12.2.2),通过方法调用转换适用(§15.12.2.3),或者是适用的可变参数方法(§15.12.2.4)。

[...]

如果在适用性测试的三个阶段中已经确定了几个适用的方法,则选择最具体的方法,如第§15.12.2.5节所述。

现在检查两种方法的源代码:
// This won't throw NPE for `obj == null`
public static String valueOf(Object obj) {
    return (obj == null) ? "null" : obj.toString();
}

// This will throw `NPE` for `data == null`
public static String valueOf(char data[]) {
    return new String(data);
}

6
因为编译器会将使用null作为参数调用的方法解析为尽可能具体的方法,而char[]Object更具体。 - Petr Janeček
5
@ceving Java有bug吗?绝对没有!Object应该被打印为null。而一个nullchar[]不能转换为String,因此会抛出异常。它们是不同的方法,这就是重点所在。到底哪里出错了呢? - Boris the Spider
1
@htor "." 代表静态方法,"#" 代表实例方法。但是,实际上并没有任何标准。 - Rohit Jain
2
如果它们有不同的名称,我会同意你的观点。但是它们有相同的名称,因此它们不是不同的方法。 - ceving
1
@ceving:它们是两种不同的重载,因此它们并不是相同的方法。但我能理解你的观点。 - Blaisorblade
显示剩余4条评论

16
在Java中有许多重载的String.valueOf方法。此外,在Java中,null具有任何和所有类型,因此任何东西(不是原语)都可以是null
因此,当您调用(String.valueOf((Object)null)时,您调用采用ObjectvalueOf方法,因为您显式地将null强制转换为Object
在第二个例子中,您没有显式地将null转换为任何特定类型,因此实际上您使用一个char[]调用了valueOf方法,导致抛出NPE异常。
JLS §15.12.2
引用:

The second step searches the type determined in the previous step for member methods. This step uses the name of the method and the types of the argument expressions to locate methods that are both accessible and applicable, that is, declarations that can be correctly invoked on the given arguments.

可能会有多个这样的方法,在这种情况下选择最具体的方法。最具体方法的描述符(签名加返回类型)是运行时执行方法分派所使用的。

在这种情况下,char[]Object更具体,因此在不进行显式null转换时调用它。

因此,最重要的是避免将原始的 null 作为参数传递给方法,而应该将它们转换为该方法的参数类型。 :) - sactiw

6
尽管我已经接受了一个答案,但我想补充一下这个问题的确切答案,因为这两个答案都集中在解释我走进的陷阱上。
(Object)null和null之间的区别在于第一个的类型被强制转换为Object,但第二个的类型并不像人们想象的那样强制转换为Object,而是可能是数组而不是对象。
因此,结论是:将(Object)null作为参数传递给方法,以确保准确地使用对象而不是任何其他对数组起作用的方法。

是的,正如Rohit已经提到的那样,方法调用将始终与最具体子类型的方法匹配。 - Sebastiaan van den Broek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接