java - 为什么使用java中的多态性从父类打印子私有字段时为空?

2022-01-24 00:00:00 inheritance polymorphism java

据了解,子类 BB 中的 getS 方法覆盖了父类 AA 中的相同方法.然而,尽管这两个类都初始化了由 getS 返回的字段 s,但它被打印为 null.为什么会这样?

As far as In understand he getS method from child class BB overrides the same method from the parent class AA. Yet although both classes have initialized the field s which is returned by getS it's printed as null. Why is this happening?

这是代码:

public class AA {
    public String getS() {
        return s;
    }
    private String s = "hello1";
    public AA() {
        System.out.println(service() + getS());
    }
    public static String service() {
        return "A service ";
    }
}

public class BB extends AA {
    private String s = "hello2";
    @Override
    public String getS() {
        return s;
    }
    public static String service() {
        return "B service ";
    }
}

public class CC {
    public static void main(String[] args) {
        BB b =new BB(); //prints "A service null"
    }
}

推荐答案

当我们调用 new SomeClass()

  1. new 运算符首先创建 SomeClass 的对象,但该对象的所有字段都设置为默认值(0、''、false、null).

  1. new operator first creates object of SomeClass but that object has all its fields set to default values (0, '', false, null).

在创建对象后,构造函数的代码被执行以正确地初始化对象(将其字段设置为适当的值,并且可能做一些其他的事情).

After object is created code of constructor is executed to initialize object properly (set its fields to proper values, and possibly do few other things).

但是如果类有父类,构造函数首先(隐式或显式)调用它的 super() 构造函数以确保所有继承的字段都正确初始化,然后我们开始使用可能依赖于的继承方法那些领域的状态.

But if class has parent class, constructor first (implicitly or explicitly) calls its super() constructor to ensure that all inherited fields are properly initialized, before we start using inherited methods which may depend on those fields state.

但方法是多态的(只要它们不是 privatestaticfinal).这意味着当我们在超类中调用被覆盖的方法时,将执行最新版本"的代码(因为多态性使用 this 实例的实际类型 - 由 new 关键字返回- 找到应该开始搜索应该执行的代码的类 - 这称为后期或 动态绑定).

But methods are polymorphic (as long as they are not private, static or final). This means that when in superclass we call method which was overridden, "newest version" of code will be executed (because polymorphism uses actual type of this instance - returned by new keyword - to locate class from which it should start searching for code which should be executed - this is called late or dynamic binding).

因此,既然您在 AA 超类中调用了 getS() 方法,但在 BB 实例上调用了方法(因为这就是 new BB 已创建),来自 BB 类的覆盖代码已被执行.问题是这段代码使用了在 BB 类中声明的 s,但是 s 还没有被初始化.让我们看看由 new BB() 调用的 BB 的默认构造函数是什么样子的:

So since you called getS() method in AA superclass but on BB instance (because that is what new BB created), overridden code from BB class was executed. Problem is that this code uses s declared in BB class, but that s wasn't initialized yet. Lets see how default constructor of BB which was invoked by new BB() looks like:

BB(){
    super(); //calls AA()
    s = "hello2"; // by default `s` holds `null` value 
                  // all initialization is moved to constructors 
                  // after superconstructor call
}

所以

  • 在执行 s = "hello2"; 之前 s (属于 BB 类) 持有 null
  • 但在 s 被初始化为 "hello2" 之前 super() 被调用(内部调用 getS())
  • 因为 getS() 是来自 BB 类的多态代码,该方法将被执行
  • 但该代码(来自 BB)使用了尚未初始化的 BB 的 s,因此它包含 null,这就是您在控制台中看到.
  • before s = "hello2"; is executed s (of BB class) holds null
  • but before s is initialized to "hello2" super() is called (which internally calls getS())
  • since getS() is polymorphic code from BB class for that method will be executed
  • but that code (from BB) uses s of BB which wasn't yet initialized, so it holds null and that is what you see in console.

因此,将可被覆盖的构造函数方法(在大多数情况下)视为不好的做法.我们应该限制自己调用非多态的方法,这意味着 private staticfinal.

Because of that it is considered bad practice (in most cases) to put in constructor methods which can be overridden. We should limit ourselves to calling methods which are not polymorphic, which means either private static or final.

相关文章