为什么在JAVA中重写的工作方式与C++有些不同

Why is overriding in JAVA working somewhat differently that from C++?

本文关键字:C++ 方式 工作 JAVA 重写 为什么      更新时间:2023-10-16

我有一些C++背景,也懂一些Java(显然远远不够)。

当我在Java或C++中看到重写行为时,它似乎没有太大区别。给出以下JAVA示例:

class Animal{
   public void move(){
      System.out.println("Animals can move");
   }
}
class Dog extends Animal{
   public void move(){
      System.out.println("Dogs can walk and run");
   }
}
public class TestDog{
   public static void main(String args[]){
      Animal a = new Animal(); // Animal reference and object
      Animal b = new Dog(); // Animal reference but Dog object
      a.move();// runs the method in Animal class
      b.move();//Runs the method in Dog class
   }
}

在Java中,使用基类引用,在C++中使用基类指针,并根据它指向的实例类型(基类对象实例或子类实例),可以实现多态性。

以上是基于您使用基类引用或指针调用实例方法,对吧?

现在我在Java中看到了这个例子。

这个Java代码中构造函数的顺序是什么?

基本上,它说如果基类函数被覆盖,那么在创建子类对象的过程中,甚至基类初始化部分也会受到影响。请参阅我从上面链接复制的以下解释:

new Son()
=>  
  Son._init
   =>  first every constructor calls super()
      Father._init
         Object._init
         who()  => is overridden, so prints "son" !!!!!
         tell(name) => name is private, so cannot be overridden => "father"
   who()  => "son"
   tell(name)  => "son"

为什么会发生这种情况?我的意思是,这符合多态性应该如何使用吗?当进行初始化的基类部分时,为什么要使用子类的重写函数?

在Java文档中http://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.5,我只找到这个:

与C++不同的是,Java编程语言在创建新类实例时不会为方法调度指定更改的规则。如果调用的方法在初始化对象的子类中被重写,那么即使在新对象完全初始化之前,也会使用这些重写方法

但我不知道背后的原因,感觉很奇怪。

有什么想法吗?

这是一种极为罕见的情况,C++比Java更能保护您免受枪击。(或者至少它有这样做的高尚意图。)

如果你试图从B的构造函数中调用基类B的可重写(虚拟)方法M,你很可能会在任何语言中自食其果。这是因为M很可能在派生类D中被重写,但在构建B的时候,D还没有被构建。因此,在调用D的构造函数之前调用了D.M。这可能意味着灾难。

所以,Java只是允许这种情况发生,使用风险自负。(如果你启用了足够的警告,你的编译器会告诉你你的生活很危险。)

C++也没有禁止这一点,但它稍微改变了自己的行为,以包含损坏,可以说:当你从构造函数中调用虚拟方法时,它并没有真正将其作为虚拟方法调用(通过VMT查找),而是直接将其作为非虚拟方法调用。

(要么是这样,要么是在B的构造函数中,它只是使用类B的VMT而不是D的VMT。现在想想,这是有道理的。但我不确定,自从我上次解决C++的这种行为以来,已经有很长时间了。)