【问题标题】:overriding protected method of Superclass覆盖超类的受保护方法
【发布时间】:2015-10-01 10:04:51
【问题描述】:

在下面的示例中,为什么字符串 b 打印 null 而字符串 c 打印“gg”。

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

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();

    }

}

【问题讨论】:

    标签: java inheritance overriding protected


    【解决方案1】:

    我相信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,因此它们要么仅依赖于已初始化的当前类,要么不依赖于任何实例变量)。

    【讨论】:

    • 这能解释为什么 c='gg' 吗?
    • 什么时候调用 BClass 的 initClass() ,如果调用它为什么设置 c 而不是 b 的值。
    • @RossDrew,你是对的......我最初的回答是不正确的。从基类调用可覆盖成员函数的断言是正确的,但我最初的推理是错误的(这是 C++ 的一个属性,在 Java 中显然不一样,我应该用一个简单的测试程序仔细检查过断言)。
    【解决方案2】:

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

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

    【讨论】:

      【解决方案3】:

      之所以会这样,是因为 AClass 的第一个构造函数设置了 b = omg!c=gg 的值。之后,当 BClass 在内存中加载时,它设置 b=null 并且 c 保持原样,即 gg,这是因为在 BClass 中,对于 b 你正在做声明以及初始化,而对于 c 你只做声明,因此由于 c 已经在内存中,它甚至不会获得它的默认值,并且由于您没有对 c 进行任何初始化,它仍保持其早期状态。

      【讨论】:

        【解决方案4】:

        截至JSF 12.5

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

        接下来,执行类 [...] 的实例变量的所有初始化程序。

        由于你的实例变量 b 被初始化为 null,之后它会再次为 null

        【讨论】:

          【解决方案5】:

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

          调试一下,下个断点,一步步走,你会发现b先设置为null,然后又变为omg!,再回到null

          【讨论】:

            猜你喜欢
            • 2013-07-01
            • 2022-07-08
            • 2012-05-14
            • 2011-08-04
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-01-26
            • 2019-01-25
            相关资源
            最近更新 更多