在Java中进行类型转换时遇到空值不会抛出异常

278
String x = (String) null;

为什么这个语句中没有异常?

String x = null;
System.out.println(x);

它打印出 null。但是 .toString() 方法应该抛出空指针异常。


你在说哪个异常,编译器? - Sajan Chandran
10个回答

384

您可以将null强制转换为任何引用类型,而不会引发任何异常。

println方法不会抛出null指针异常,因为它首先检查对象是否为空。如果为空,则只打印字符串"null"。否则,它会调用该对象的toString方法。

添加更多细节: 内部的打印方法在输入对象上调用String.valueOf(object)方法。在valueOf方法中,这个检查有助于避免空指针异常:

return (obj == null) ? "null" : obj.toString();

针对您的疑惑,调用空对象上的任何方法应该会抛出一个空指针异常,如果不是特殊情况的话。


1
@JunedAhsan 有哪些特殊情况会导致它不抛出 NPE? - Holloway
@Trengot 请检查Peter在下面回答中提到的一个。 - Juned Ahsan

163

您可以将null转换为任何引用类型。您还可以调用处理null作为参数的方法,例如System.out.println(Object),但您不能引用null值并调用它的方法。

顺便说一下,有一个棘手的情况,其中似乎可以在null值上调用静态方法。

Thread t = null;
t.yield(); // Calls static method Thread.yield() so this runs fine.

14
哇,如果有人问我,我会百分之百地肯定它会抛出异常。这确实是一个棘手的情况。 - Magnilex
2
@Magnilex:当然!这是典型的OCPJP考试陷阱题。 - ccpizza
3
这不是因为编译器会将字节码中的t.yield() -> Thread.yeld()进行“优化”吗?这类似于final int i = 1; while (i == 1)被优化为while(true)的方式。 - SGal
@SGal 这甚至更好,因为第二个优化据我所知不是强制性的,而第一个有点是。 - Paul Stelian

45

这是设计上的考虑。你可以将 null 转换为任何引用类型,否则你将无法将其赋值给引用变量。


1
谢谢!这个关于将null赋值给引用变量的笔记真的很有帮助! - Max

26

在下面这种方法重载的情况下,强制转换 null 值是必需的。如果将 null 传递给这些重载方法,则编译器不知道如何消除歧义,因此我们需要在这些情况下对 null 进行类型转换。

class A {
  public void foo(Long l) {
    // do something with l
  }
  public void foo(String s) {
    // do something with s      
  }
}
new A().foo((String)null);
new A().foo((Long)null);
否则你将无法调用所需的方法。

在大多数情况下,强制类型转换是隐式的,例如String bar = null;null值转换为String。到目前为止,我只需要在一个测试中显式地转换null,其中一个方法被重载并且我想要测试其对null输入的行为。不过,知道这一点还是很好的,因为我正准备写一个类似的答案,然后发现了你的答案。 - Vlasec
有趣的是,在这些情况下,l instanceof Longs instanceof String 将返回 false - Attila Tanyi

8

Println(Object) uses String.valueOf()

public static String valueOf(Object obj) {
    return (obj == null) ? "null" : obj.toString();
}

Print(String) 进行空值检查。

public void print(String s) {
    if (s == null) {
        s = "null";
    }
    write(s);
}

8

4

这种语言特性在这种情况下非常方便。

public String getName() {
  return (String) memberHashMap.get("Name");
}

如果memberHashMap.get("Name")返回null,您仍希望上述方法返回null而不抛出异常。无论类是什么,null就是null。

3

正如其他人所写,你可以将null转换为任何类型。通常情况下,你不需要这样做,你可以编写:

String nullString = null;

不需要添加转换。

但是有时候这样的转换也是有意义的:

a) 如果你想要确保调用一个特定的方法,比如:

void foo(String bar) {  ... }
void foo(Object bar) {  ... }

如果您输入的话,这会有所不同。

foo((String) null) vs. foo(null)
b) 如果您打算使用IDE生成代码;例如,我通常编写单元测试如下:
@Test(expected=NullPointerException.class)
public testCtorWithNullWhatever() {
    new MyClassUnderTest((Whatever) null);
}

我正在进行TDD,这意味着类“MyClassUnderTest”可能还不存在。通过编写该代码,我可以使用我的IDE首先生成新类;然后生成一个接受“Whatever”参数的构造函数“开箱即用”-IDE可以根据我的测试推断构造函数应该正好接受一个类型为Whatever的参数。


2

打印

打印一个对象。由String.valueOf(Object)方法产生的字符串被转换为字节。

ValueOf

如果参数为null,则返回一个等于“null”的字符串;否则,返回obj.toString()的值。

当对象为null时,它将简单地返回一个值为“null”的字符串。


2

当使用可能会产生歧义的方法时,这非常方便。例如:JDialog具有以下签名的构造函数:

JDialog(Frame, String, boolean, GraphicsConfiguration)
JDialog(Dialog, String, boolean, GraphicsConfiguration)

我需要使用这个构造函数,因为我想设置GraphicsConfiguration,但是我没有这个对话框的父级,所以第一个参数应该是null。使用

JDialog(null, String, boolean, Graphicsconfiguration) 

这里存在歧义,因此我可以通过将null转换为其中一个支持的类型来缩小调用范围:

JDialog((Frame) null, String, boolean, GraphicsConfiguration)

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