没有为包含对象的引用的未分配数组元素引发异常

No exception raised for referenced unassigned array element containing an object

物料需求计划

class B {
    static int v;
    
    public B(int i) {
        System.out.format("Constructor called with value %d\n", i);
        v=i;
    }
}

public class A {
    static B[] c;
    
    A(){
        c=new B[5];
        
        c[1]=new B(1);
    
        for (int i=0; i<3; i++) {
            System.out.format("c[%d] is %d\n", i, c[i].v);
        }
        
        c[2]=new B(2);
        
        for (int i=0; i<3; i++) {
            System.out.format("c[%d] is %d\n", i, c[i].v);
        }
    }
    
    public static void main(String[] args) {
        new A();
    }
}

Output is:

Constructor called with value 1
c[0] is 1
c[1] is 1
c[2] is 1
Constructor called with value 2
c[0] is 2
c[1] is 2
c[2] is 2

期望通过引用未分配的数组元素引发异常,例如c[0].先前的赋值也错误地更改了数组值。 c[0] 从未被赋值,但在上面的输出中取值 1 和 2。


public class A {
    static String[] c;
    
    A(){
        c=new String[5];
        
        c[0]=new String("alpha");
    
        for (int i=0; i<3; i++) {
            System.out.format("c[%d] is %s\n", i, c[i]);
        }
        
        c[1]=new String("beta");
        
        for (int i=0; i<3; i++) {
            System.out.format("c[%d] is %s\n", i, c[i]);
        }
    }
    
    public static void main(String[] args) {
        new A();
    }
}


Output for the above is:

c[0] is alpha
c[1] is null
c[2] is null
c[0] is alpha
c[1] is beta
c[2] is null

上例中的 String 对象有不同的行为。

所以问题是为什么当 c[i]nullc[i].v 不会导致 NullPointerException

让我们从稍微简单一点的开始:

B b = null;
System.out.println(b.v);

这不会抛出 NullPointerException

为什么?

由于Bv字段是static,我们不需要取消引用b的值来获取v的值. v 的值与 B.

的任何特定实例无关

所以,事实上,b.vB.v是等价的。


在更一般的情况下,考虑 <expr> 是一些静态类型为 B 的表达式。那么:

  V v = <expr>.v

与以下效果相同:

  B temp = <expr>;
  V v = B.v;

换句话说,计算表达式并丢弃其值。然后获取静态字段的值。但是由于 temp 没有被取消引用(因为它不需要 ),在表达式计算为零的情况下不会有 NPE ...它在你的例子中。


您的字符串示例的不同之处在于您打印的是 String 实例的状态,而不是 static 字段的状态。并且在字符串连接中没有出现 NPE,因为 + 运算符将 null 映射到 "null" 而不是调用 null.toString().


这里的底线是使用实例引用访问静态字段不是一个好主意。因为语法不符合您的预期。

确实有些 Java 样式检查器/静态分析器会将其标记为糟糕的样式或可能的错误。