【问题标题】:Accessing instance members in constructors在构造函数中访问实例成员
【发布时间】:2012-08-21 14:53:02
【问题描述】:

我在一本书中读到实例成员只有在超级构造函数运行后才能访问。

我偶然发现了以下代码:

class Parent {

    Parent() {
        printIt();
    }

    void printIt() {
        System.out.println("I'm in a overridden method. Great.");
    }
}

class Child extends Parent {

    int i = 100;

    public static void main(String[] args) {
        Parent p = new Child();
        p.printIt();
    }

    void printIt() {
        System.out.print(i + " ");
    }
}

然后打印出来:

0 100

我的问题是:

如果实例成员只有在超级构造函数运行后才能访问,那为什么在执行Parent类的printIt()方法时(由于多态,实际上是Child的printIt()),它能够即使Parent的构造函数还没有执行完,也访问Child未初始化的实例变量i?

我错过了什么?

【问题讨论】:

    标签: java constructor instance-variables super


    【解决方案1】:

    即使Parent的构造函数还没有执行完毕,它也能访问Child未初始化的实例变量i?

    您可以访问它,但在它被初始化之前(这不是您通常想要的)。

    变量的“空格”已经就位(毕竟您确实有一个实例),但是将其初始化为正确起始值的代码尚未运行。所以它将全部为 null、false 和 0。

    因此,类中的方法(“printIt”)在对象生命周期的一个尴尬点被调用(在初始化程序运行之前,在“半完成”实例上)。这就是你读到的警告想要表达的意思。

    【讨论】:

      【解决方案2】:

      我在一本书中读到实例成员只有在超级构造函数运行后才能访问。

      你的书是错的(如果它真的这么说的话)。一旦施工开始,它们就可以随时访问。然而,直到超级构造函数运行之后,它们才初始化。所以你打印的是默认值:null、零或 false。

      【讨论】:

      • “一旦构造开始,它们就可以随时访问。但是直到超级构造函数运行后它们才被初始化。” --- 好点。 +1
      【解决方案3】:

      覆盖发生在您的代码中。在运行时考虑对象。因此,调用了 Child 的 printIt()。此时,“i”的值未知,但默认值为“0”,因为它是一个实例变量。完成后,调用 p.printIt(),调用 Child 的 printIt(),此时读取 int i=100 并打印 100。

      因此输出应该是0 100

      【讨论】:

        【解决方案4】:

        我认为你的例子误导了你。事实上,超级构造函数之前运行过,你可以通过下面的修改示例看到这一点。另外澄清一下,成员值是可访问的,但它们可能尚未初始化。

        class Parent {
        
            int i = 0;
        
            Parent() {
                i = 1;
                printIt();
            }
        
            void printIt() {
                System.out.println("I'm in a overridden method. Great. i = " + i);
            }
        }
        
        class Child extends Parent {
            public static void main(String[] args) {
                Parent p = new Child();
                p.printIt();
            }
        
            void printIt() {
                System.out.print(i + " ");
            }
        }
        

        【讨论】:

          【解决方案5】:

          对象中的字段在首次创建对象时被初始化为默认值 null 或 0,然后您的构造函数实际运行,它首先调用超级构造函数。

          遗憾的是,您无法通过将构造函数编写为

          来解决此问题
          Child() {
            i=100;
            super();
          }
          

          如果无法做到这一点,就无法在父构造函数的覆盖方法调用中使用子i 字段之前对其进行设置。

          不过,值得了解一些解决此问题的方法:

          方法是将i 隐藏在抽象getter 后面,并提供一个静态工厂函数来创建覆盖getI 的新实例。

          public class Child extends Parent {
          
             protected abstract getI();
          
             @Override void printIt() {
               System.out.print("i = " + i);
             }
          
             static Child create(final int i) {
                return new Child() {
                   int getI() { return i; }
                }
             }
          }
          
          Child child = Child.create(100);
          

          另一种方法是将printIt 与父/子层次结构分离。那么你就可以 在调用 Parent 构造函数之前创建打印机。 (通常这种技巧可以用来完全去除 Child ,只剩下 Parent 类和组件 - 你最终会使用组合而不是继承。)

          class Parent {
             public interface Printer {
               void printIt();
             }
          
             public class DefaultPrinter extends Printer {
               @Override void printIt() { 
                 System.out.println("Default Printer...");
               }
             }
          
             Parent() {
               this(new DefaultPrinter());
             }
          
             Parent(Printer p ) {
               this.printer = p;
               printIt();
             }
          
             void printIt() {
               p.printIt();
             }
          }
          
          public class Child extends Parent {
             public class ChildPrinter implements Parent.Printer {
               final int i = 100;
               @Override void printIt() {
                 System.out.println("i = "+i);
               }
             }
          
             Child() {
               super( new Printer() );
             }
          }
          

          【讨论】:

            猜你喜欢
            • 2021-11-17
            • 2020-07-11
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多