覆盖超类的受保护方法

时间:2015-07-13 08:10:57

标签: java inheritance override protected

在下面的例子中,为什么String b打印null,而String c打印“gg”。

如果我错了,每当子类(BClass)覆盖超类(AClass)的受保护方法(即initClass())时,请纠正我。如果您实例化子类。超类必须使用子类指定的override方法。

public class Example {

    public class AClass {

        private String a;

        public AClass() {
            initClass();
        }

        protected void initClass() {
            a = "randomtext";
        }
    }

    public class BClass extends AClass {

        private String b = null; 
        private String c;          


        @Override
        protected void initClass() {
            b = "omg!";
            c = "gg";
        }

        public void bValue() {
            System.out.println(b);   // prints null
            System.out.println(c);  // prints "gg"
        }
    }

    public static void main(String[] args) {
        Example.BClass b = new Example().new BClass();
        b.bValue();

    }

}

5 个答案:

答案 0 :(得分:3)

截至JSF 12.5

在示例中,您可以看到执行顺序。第一步是将构造函数调用到Object构造函数。 之后会发生这种情况:

  

接下来,执行类[...]的实例变量的所有初始值设定项。

由于您的实例变量b初始化为null,因此之后将再次为null

答案 1 :(得分:2)

这是因为在初始化ClassB的字段之前调用了超类构造函数。因此调用initClass()方法设置b = "omg!",但是当超类构造函数返回时,b被初始化为ClassB中声明的值null }。

要进行调试,设置一个断点并逐步进行,您会发现b首先设置为null,然后更改为omg!,然后返回{{ 1}}。

答案 2 :(得分:2)

已经有几个关于发生了什么的正确答案。我只是想补充一点,从构造函数调用重写方法通常是不好的做法(当然,除非你确切知道你在做什么)。正如您所看到的,子类在调用其实例方法时可能尚未完全初始化(子类构造函数逻辑尚未执行,因此在未构造的危险对象上调用有效覆盖的方法)可能会导致混淆这个问题中描述的那个。

在构造函数中编写初始化逻辑要好得多,如果它太长则将其分配给从构造函数调用的几个私有方法。

答案 3 :(得分:0)

我相信this example解释了这个问题:

public class Main {
    private static class PrintOnCreate {
        public PrintOnCreate(String message) {
            System.out.println(message);
        }
    }

    private static class BaseClass {
        private PrintOnCreate member =
            new PrintOnCreate("BaseClass: member initialization");

        static {
            System.out.println("BaseClass: static initialization");
        }

        public BaseClass() {
            System.out.println("BaseClass: constructor");
            memberCalledFromConstructor();
        }

        public void memberCalledFromConstructor() {
            System.out.println("BaseClass: member called from constructor");
        }
    }

    private static class DerivedClass extends BaseClass {
        private PrintOnCreate member =
            new PrintOnCreate("DerivedClass: member initialization");

        static {
            System.out.println("DerivedClass: static initialization");
        }

        public DerivedClass() {
            System.out.println("DerivedClass: constructor");
        }

        @Override
        public void memberCalledFromConstructor() {
            System.out.println("DerivedClass: member called from constructor");
        }
    }


    public static void main (String[] args) {
        BaseClass obj = new DerivedClass();
    }
}

该程序的输出是:

BaseClass: static initialization
DerivedClass: static initialization
BaseClass: member initialization
BaseClass: constructor
DerivedClass: member called from constructor
DerivedClass: member initialization
DerivedClass: constructor

...这表明派生类的成员在基类的构造函数之后被初始化(并且派生类的成员函数的调用已经完成)。这也证明了从构造函数调用可覆盖函数的关键危险,即可以在初始化它所依赖的类的成员之前调用该函数。出于这个原因,构造函数通常应该避免调用成员函数(当它们执行时,这些函数应该是finalstatic,这样它们或者只依赖于已经初始化的当前类或者没有任何实例变量。)

答案 4 :(得分:0)

这种情况正是这样发生的,因为,首先是AClass的构造函数,它设置了b = omg!c=gg的值。之后当BClass在内存中加载时它设置b=null并且c保持原样gg,这是因为,因为在BClass中,对于b,你正在进行声明和初始化,对于c你正在做只有声明,因为c已经在内存中它甚至不会获得它的默认值,并且由于你没有对c进行任何初始化,它仍然保留在它之前的状态。