Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sockets/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 超类的重写保护方法_Java_Inheritance_Overriding_Protected - Fatal编程技术网

Java 超类的重写保护方法

Java 超类的重写保护方法,java,inheritance,overriding,protected,Java,Inheritance,Overriding,Protected,在下面的示例中,为什么字符串b打印null而字符串c打印“gg” 如果我错了,请纠正我,只要子类(BClass)重写了超类(AClass)的受保护方法(即initClass()),如果您实例化了该子类。超类必须使用子类指定的重写方法 public class Example { public class AClass { private String a; public AClass() { initClass();

在下面的示例中,为什么字符串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();

    }

}
我认为这就是问题的原因:

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

。。。这表明派生类的成员是在基类的构造函数(以及派生类的成员函数的调用完成)之后初始化的。这还说明了从构造函数调用可重写函数的一个关键危险,即该函数可以在它所依赖的类的成员初始化之前调用。因此,构造函数通常应该避免调用成员函数(当它们调用时,这些函数应该是
final
static
,这样它们要么仅依赖于已初始化的当前类,要么不依赖于任何实例变量).

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

要进行调试,请设置一个断点并逐步进行,您会发现
b
首先设置为
null
,然后更改为
omg然后返回到自的
null

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

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


由于您的实例变量b被初始化为null,因此之后它将再次为null,这是因为AClass的第一个构造函数将值设置为
b=omg
c=gg
。在这之后,当b类在内存中加载时,它设置为
b=null
,而c保持原样,即gg,这是因为在b类中,对于b类,您执行声明和初始化,对于c类,您只执行声明,因此,由于c已经在内存中,它甚至不会得到它的默认值,并且由于您没有对c进行任何初始化,它仍然保持其早期状态

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


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

这是否解释了为什么c='gg'?何时调用BClass的initClass(),如果调用它,为什么设置c值而不是b值。@Rossdew,你是对的。。。我最初的回答是不正确的。从基类调用可重写成员函数的断言是CORRET,但我原来的推理是错误的(这是C++中的一个属性,显然在java中不一样,在断言之前我应该用一个简单的测试程序来双重检查)。