java,继承和覆盖字段中的初始化顺序

时间:2014-09-22 08:18:57

标签: java inheritance initialization override

class A {
    static int c = 7;
    int a = 0;

    public A() {
        c = c + 2;
        a = c;
    }

    public int m(int x){
        return a * --x;
    }

}


class B extends A{
    int a = 13;
    static int c = 1;
    public B(){
        c = c*3;
        a = c;

    }

    public int m (int x){
        return --a * ++x;
    }

    public static void main(String[] args) {
        A o1 = new A();
        A o2 = new B();
        B o3 = new B();

        System.out.println(o1.a);
        System.out.println(o1.m(o1.a));
        System.out.println(o1.a);
        System.out.println(c);
        System.out.println(o2.a);
        System.out.println(o2.m(5));
        System.out.println(o2.a);
        System.out.println(c);
        System.out.println(o3.a);

    }
}

我知道动态和静态绑定,但我不明白它在这个例子中是如何工作的(覆盖静态字段)。我真的很惊讶为什么o2.m = 12,而o3.a = 9!这些类的c字段是否分开? B构造函数中使用哪些字段?如果我写了A o1 = new B()或B o1 = new B()?请解释它是如何工作的。

2 个答案:

答案 0 :(得分:4)

如果我写了A o1 = new B()B o1 = new B() ,会有区别吗。

是的,

A o1 = new B(); calls methods of `B` and fields of A.
B o1 = new B(); calls methods of `B` and fields of B. 

,即根据引用类型解析字段引用,并根据对象类型解析(动态)方法调用。

答案 1 :(得分:0)

诀窍在于,在您定义它的方式中,B有2个名为a的成员。考虑一下:

按如下方法向B类添加方法:

public int x() {
    return this.a;
}
public int y() {
    return super.a;
}

如果你那么做

public static void main(String[] args) {
    B b = new B();

    System.out.println(b.x());
    System.out.println(b.y());

}

输出将是:

3
9

您在B类中对a的定义不会替换或覆盖A类中的a隐藏它。正如您所看到的,您仍然可以通过调用super来访问它,但它确实会导致无法维护的代码,因此不鼓励。

你应该做些几件事来避免这种不幸事故:

  • 不要给多个字段赋予相同的名称,因为它会使您的代码不那么清晰。
  • 让你的对象成员private,除非你真的有令人信服的理由不这样做。您的示例已经证明,即使protected访问也可以使事情变得不透明。
  • 为成员使用getter和setter。如果您的对象属于B类型,那么您的对象是定义为A还是B并不重要。

你是否写下这个:

A obj = new B();
obj.getA();

或者这个:

B obj = new B();
obj.getA();

对您的输出无关紧要,因为您的方法调用将始终将getter称为" low"尽可能在类层次结构中,它始终为B(如果方法可用),因为对象的运行时类型为B。最后一点实际上是@TheLostMind的要点