字符数组和整型数组的区别

18

当我尝试打印未初始化的 静态字符数组 时,会出现运行时错误(空指针异常),而未初始化的 静态整型数组 则会给出 null 值。为什么?

public class abc {
    static int arr[];
    static char ch[];

    public static void main(String[] args) {
        System.out.println(ch); //it gives null pointer exception at run time
        System.out.println(arr); //it gives output as "null".
        }
    }

7
可以肯定的是,println() 的字符数组版本是经过重载的,它会尝试将数组作为字符串打印出来,而 int[] 版本则仅将其作为数组打印出来。当使用 char * 或者通用的 void * 指针调用时,请参考 C++ 中的 std::cout::operator<< - The Paramagnetic Croissant
是的,这都与println重载有关,与数组本身无关。 int []char []本质上是相同的,除了int是4字节有符号数据,而char是2字节无符号数据。 - Hot Licks
4个回答

15
答案存在于PrintWriter源代码中(其中System.out是一个实例)。
首先,未初始化的数组作为引用变量会被赋予默认值nullprintln(char[])方法尝试调用传入的数组的.length属性。由于传入的数组为null,因此导致了NullPointerException异常。最终,println(char[])方法会调用write(char[])方法。
public void write(char buf[]) {
    write(buf, 0, buf.length);
}

println没有重载方法能匹配上int[],但是有一个println(Object)。于是println调用println(Object),最终它会调用String.valueOf,传入了null引用,所以String.valueOf接收到null参数并返回String类型的字符串"null"println(Object)又会调用print(Object)

public void print(Object obj) {
    write(String.valueOf(obj));
}

异常处理很耗费资源,为什么不直接检查是否为空?我知道这是一个设计师的问题,但我很好奇。 - Engineer2021
打印也很贵,我猜是吧? - Octavia Togami
1
System.out是一个PrintStream,而不是PrintWriter。 - Boann

15

System.outPrintStream类的实例,该类有几个重载的println方法。在你的情况下:

  1. System.out.println(ch);使用了public void println(char x[])
  2. System.out.println(arr);使用了public void println(Object x)(因为没有public void println(int[] x)方法,所以println可以使用的最接近的类型是Object)。

第二种方法使用了

String.valueOf(x);

为了获得我们想要打印的对象的字符串表示形式,valueOf 方法的代码如下:

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

因此它是空值安全的(如果引用为null,则会返回字符串"null")。

第一个方法在某种程度上使用了

public void write(char cbuf[]) throws IOException {
    write(cbuf, 0, cbuf.length);
                 //    ^^^^^^^ this throws NPE
}

由于cbufnull,所以cbuf.length会抛出NullPointerException异常,因为null没有length(或任何其他)字段。


很难说。也许Java的作者认为print(Object)方法主要用于打印对象的状态(借助其toString方法),因此打印null感觉正确,但从print(char[])中,他们期望打印char数组中的确切字符,由于char[]与String相比不是那么常见的类型,所以将其设置为null更像是错误而不是预期状态,因此他们决定通过抛出NPE来通知程序员(并停止执行有错误的程序)。但这不一定是真的。您需要问Java设计师。 - Pshemo
@usr,如果你想使用“Object”重载,那么你也可以这样做:System.out.println((Object) chr) - Octavia Togami
@ıɯɐƃoʇǝızuǝʞ 是的,但这将打印数组的toString()结果,因此我们将看到类似于[C@1db9742的内容,而不是数组的内容。换句话说,println(new char[]{'f','o','o'})将打印foo,而println((Object)new char[]{'f','o','o'})将打印[C@1db9742 - Pshemo
在Java的世界中,NPE难道不总是被认为是一个bug吗?这并不像库开发人员在这里已经决定抛出这个异常。他们没有考虑到这种情况,在内部代码中崩溃。幸运的是,这不是一个损坏或安全问题。 - usr
1
这是一个既存在于调用方又存在于库中的错误。 - usr
显示剩余5条评论

2
这是两种不同的方法,它们的API完整地描述了它们的行为。 public void println(Object x) 首先调用 String.valueOf 方法,如果 x 为 null,则返回 "null"。
参见:

public void print(char[] c) 调用了 print(char[] c) 方法,如果 c 为 null,则会抛出 NullPointerException 异常。

参见:


0

您实际上正在调用两个独立的、重载的System.out.println()方法。public void println(char[] x) 根据文档进行了重载。没有为 int[] 数组存在特定的 println(Object x) 重载。

因此,当在整数数组上调用 println() 时,将调用 public void println(Object x)。根据 Javadocs:

该方法首先调用 String.valueOf(x) 来获取所打印对象的字符串值,然后表现得好像它调用 print(String) 然后是 println()。

由于字符串的值为 null,该方法会打印 null。

println() 方法以字符数组作为参数时,其工作方式有所不同。从表面上看,该方法本身似乎相似:

打印一个字符数组,然后终止该行。该方法表现得好像它调用 print(char[]),然后是 println()。

然而,print(char[]) 方法在关键方面表现出了相当不同之处:

打印字符数组。根据平台的默认字符编码将字符转换为字节,并以与write(int)方法完全相同的方式写入这些字节。
因此,它调用了不同的方法。print(char[])的文档明确说明,在您的情况下,会抛出异常:
"如果[s输入参数]为空,则抛出NullPointerException"
因此,行为差异的原因就在这里。
有关println和print方法的更多信息,请参见Javadocs: http://docs.oracle.com/javase/7/docs/api/java/io/PrintStream.html#println()

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