【问题标题】:Java Inheritance strange behaviourJava继承奇怪的行为
【发布时间】:2018-10-12 13:21:00
【问题描述】:

以下代码将输出打印为 40 而不是 30。我无法找出解决方案。请帮忙写代码。

class Base {

  int value = 0;

    Base() {
      addValue();
    }

    void addValue() {
      value += 10;
    }

    int getValue() {
      return value;
    }
}

class Derived extends Base {

  Derived() {
    addValue();
  }

  void addValue() {
    value +=  20;
  }
}

public class Test{

  public static void main(String[] args) {
    Base b = new Derived();
    System.out.println(b.getValue());
  }
}

Derived 构造函数中的隐式超级引用调用Base 构造函数,后者又调用Base 类中的方法addValue() 导致值变量为10,然后Derived 类中的addValue() 应将值加20 10. 所以最终输出是30。

但代码打印的是 40。

【问题讨论】:

  • (我知道这在 C++ 中是不同的,它不会遵循构造函数对更多派生类的覆盖,因为更多派生类不会被初始化。)
  • 请为您的问题选择一个答案,以便其他人可以轻松找到它。
  • @TomHawtin 你是说同样的 C++ 代码会输出 30 吗?在那种情况下,我绝对可以看到混乱来自哪里。
  • @BrandonG 是的。 (刚刚尝试过并将其添加为答案。)

标签: java oop inheritance


【解决方案1】:

addValue 方法在 Derived 类中被覆盖。当方法被覆盖时,调用此类实例上的方法总是调用被覆盖的版本,即使调用发生在基类中。

【讨论】:

  • 是的,但是方法调用是在构造函数中完成的,该构造函数在对象创建时开始执行 new Derived() 并调用 Derived 类构造函数,该构造函数再次调用 Base 类构造函数,因为隐式 super() 引用和 Base类 addValue() 首先执行,然后是派生类中的 addValue()。
  • 不,这不是它的工作方式。 Derived 对象从一开始就是一个 Derived 对象,所有方法调用都会调用 Derived 类中的实现,即使调用发生在 Derived 构造函数执行之前。
【解决方案2】:

Derived类中,方法void addValue()指向Derived中定义的方法,而不是Base中定义的方法

【讨论】:

    【解决方案3】:

    很可能在您扩展基类时

    class Derived extends Base {
    
      Derived() {
    
        addValue();
    
      }
    
      void addValue() { //here
    
        value +=  20;
    
      }
    
    }
    

    您将方法名称与Base Class 中的方法名称相同,这会覆盖默认名称:

       void addValue() {
          value += 10;
        }
    

    所以,输出是 40 -> 20 + 20

    【讨论】:

      【解决方案4】:

      正如其他人在我之前到达那里一样:addValueDerived 中被覆盖,因为它是具有相同名称和相同参数类型的可访问方法。通常,您会将@Override 注释添加到覆盖方法中。即使基类仍在构建中,派生类中的方法。

      并非所有语言都做同样的事情。例如,C++ 不会在基构造函数仍在运行时从派生类运行重写的方法。 C++ 中的等效程序确实显示 30。

      #include <iostream>
      
      class Base {
      public:
          int value;
      
          Base() : value(0) {
            addValue();
          }
      
          virtual void addValue() {
            value += 10;
          }
      
          int getValue() {
            return value;
          }
      };
      
      class Derived : public Base {
      public:
        Derived() {
          addValue();
        }
      
        virtual void addValue() {
          value +=  20;
        }
      };
      
      int main() {
          Base *b = new Derived();
          std::cout << b->getValue() << std::endl;
      }
      

      【讨论】:

        【解决方案5】:

        addValue()Derived 类中被覆盖,您创建了一个Derived 类对象。因此,每当此实例调用 addValue() 时。总是方法被称为Derived 类。所以这就是为什么派生类的addValue() 调用了两次。一次在Base 类构造函数中,第二次在Derived 类构造函数中。

        【讨论】:

        • 但是派生类构造函数将使用 super() 引用隐式指向父类构造函数,并且基类构造函数开始执行,它应该在基类中调用 addValue()
        • 是的。在基类中调用 addValue() 时,它执行派生类函数,因为它具有派生类的引用。如果派生类中没有覆盖函数,则调用基类函数。
        • 如果您将添加值代码放在两个构造函数中,例如在基本构造函数中写入代码 value+=10,在派生构造函数中写入 value+=20。那么你的最终值将是 30。
        【解决方案6】:

        正如其他人之前讨论的那样,方法覆盖属性很好地适用于基类中的方法调用。

        通过这个java应用运行时多态,即根据运行时调用的对象,决定要执行的方法。 例如,

        • 如果使用超类对象调用方法,则使用超类 方法将被执行。
        • 如果使用子类对象来调用 方法,然后子类方法将被执行。

        如果我们不想重写,我们可以将父方法设为private,而不是默认的私有包。现在我们将得到 30 作为输出。 因为方法不能被继承所以不能被覆盖

        class Base {
        
            int value = 0;
        
            Base() {
                addValue();
            }
        
            private void addValue() {
                value += 10;
            }
        
            int getValue() {
                return value;
            }
        }
        
        class Derived extends Base {
        
            Derived() {
                addValue();
            }
        
            void addValue() {
                value +=  20;
            }
        }
        
        public class Test{
        
            public static void main(String[] args) {
                Base b = new Derived();
                System.out.println(b.getValue());
            }
        }
        

        现在,输出为 30

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2012-11-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多