4

我声明并立即实例化的字段是null. 这是一个示例代码:

public class NullFieldSSCCE {

    static abstract class Parent {
        List<String> values;
        Parent() {
            values = getValues();
        }

        protected abstract List<String> getValues();   
    }

    static class Child extends Parent {

        String param1="test1";
        String param2="test2";

        Child() {
        }

        @Override
        protected List<String> getValues() {
            return Arrays.asList( new String[] {param1, param2} );
        }
    }

    public static void main(String[] args) {
        Child child = new Child();

        System.out.println("Child p1="+child.values.get(0)+", p2="+child.values.get(1));
    }

}

运行结果是

Child p1=null, p2=null

虽然我希望

Child p1=test1, p2=test2

这怎么可能?这些字段是在类的同时实例化的,不是吗?

4

1 回答 1

7

怎么了

您遇到的情况在JLS中的章节8.3.28.8. 您可以在此处找到一些不太详细但可能更易于阅读的信息。

这里的问题是对象的初始化顺序。

这是您的示例的初始化顺序:

  1. Parent字段被实例化:values成为ArrayList
  2. Child构造函数调用super(),因此调用Parent构造函数
  3. getValues()叫做; 在这种情况下,它是Child.getValues();
  4. Child字段被实例化:param1param2分配它们的值
  5. Child构造函数继续 - 在你的情况下什么也不做

您假设给定层次结构中所有类的所有字段都同时实例化,这发生4在. 不幸的是,这个假设是错误的。31

学习初始化顺序

即使您已经阅读了我给您的链接,您也可能不确定在特定情况下会如何工作。每当您对初始化顺序有疑问时,请毫不犹豫地添加printfs 来检查它。这次我为你做了:

public class NullFieldSSCCE {

    static abstract class Parent {
        List<String> values = new ArrayList<String>() {{
          System.out.println("Parent.values instantiation");  
        }};

        Parent() {
            System.out.println("Parent()");
            values.addAll(getValues());
        }

        protected abstract List<String> getValues();   
    }

    static class Child extends Parent {

        String param1="test1";
        String param2="test2";
        Object param3 = new Object() {{System.out.println("Child.param3 instantiation"); }};

        Child() {
            System.out.println("Child()");
        }

        @Override
        protected List<String> getValues() {
            System.out.println("Child.getValues()");
            return Arrays.asList( new String[] {param1, param2} );
        }
    }

    public static void main(String[] args) {
        System.out.println("start");
        Child child = new Child();

        System.out.println("Child p1="+child.values.get(0)+", p2="+child.values.get(1));
    }

}

输出是:

start
Parent.values instantiation
Parent()
Child.getValues()
Child.param3 instantiation
Child()
Child p1=null, p2=null
于 2013-10-21T07:43:48.290 回答