Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 即使对象是子类,也要调用超类方法_Java_Oop_Polymorphism_Overriding - Fatal编程技术网

Java 即使对象是子类,也要调用超类方法

Java 即使对象是子类,也要调用超类方法,java,oop,polymorphism,overriding,Java,Oop,Polymorphism,Overriding,我在玩简单的重载覆盖规则,发现了一些有趣的东西。这是我的密码 package com.demo; public class Animal { private void eat() { System.out.println("animal eating"); } public static void main(String args[]) { Animal a = new Horse(); a.eat(); }

我在玩简单的重载覆盖规则,发现了一些有趣的东西。这是我的密码

package com.demo;

public class Animal {

    private void eat() {
        System.out.println("animal eating");
    }

    public static void main(String args[]) {

        Animal a = new Horse();
        a.eat();
    }
}

class Horse extends Animal {
    public void eat() {
        System.out.println("Horse eating");
    }
}
该程序输出以下结果

吃动物

以下是我所知道的:

  • 由于我们有
    private void eat()
    方法,它不一定会在子类中被访问,因此这里不会出现方法重写的问题,因为它定义得很清楚
  • 既然这不是方法重写,它肯定不会从Horse类调用
    public void eat()
    方法
  • 现在我们的声明
    animala=newhorse()由于多态性而有效

为什么
a.eat()
调用
Animal
类中的方法?我们正在创建一个
Horse
对象,那么为什么要调用Animal类的方法呢?

标记为
private
的方法不能在子类中重写,因为它们对子类不可见。从某种意义上说,你的
类根本不知道
动物
有一个
方法,因为它被标记为
私有
。因此,java不认为<代码>马<代码>代码>吃< /C> >方法是一个重写。这主要是作为安全功能设计的。如果一个类有一个标记为
private
的方法,则假设该方法只用于类内部,外部世界完全无法访问该方法。如果子类可以重写
private
方法,那么它可能会以意外的方式更改超类的行为,这是(1)不可预料的,(2)潜在的安全风险


因为Java假定类的
private
方法不会被重写,所以每当您通过某种类型的引用调用
private
方法时,Java总是使用引用的类型来确定要调用的方法,而不是使用该引用指向的对象类型来确定要调用的方法。这里,引用的类型是
动物
,因此这就是被调用的方法,即使该引用指向

,我不确定是否理解您的混淆。根据您所知:

你是对的,
Horse.eat()
没有覆盖
Animal.eat()
(因为它是私有的)。换句话说,当您调用
anAnimal.eat()
时,不会发生延迟绑定,因此,您只需调用
Animal.eat()
,这就是您所看到的


从您的另一条评论来看,您的困惑似乎是编译器如何决定调用什么。下面是一个非常高层次的解释:

当编译器看到
动物a=。。。;a、 吃(),它将尝试解析要调用的内容

例如,如果它看到
eat()
是一个静态方法,给定
a
是对
Animal
的引用,编译器将把它转换为调用
Animal.eat()

如果它是一个实例方法,并且遇到一个可能已被子类重写的方法,编译器所做的是,它不会生成调用特定方法的指令。相反,它将生成从vtable执行某种查找的指令。从概念上讲,每个对象都有一个小表,表的键是方法签名,值是要调用的实际方法的引用。例如,如果在您的情况下,
Animal.eat()
不是私有的,那么马的vtable将包含类似于
[“eat()”->“Horse.eat()”]
的内容。因此,在运行时,给定一个
Animal
引用并调用
eat()
,实际发生的是:使用
eat()
从引用对象的vtable中查找,并调用关联的方法。(如果ref指向一匹
,则关联的方法将是
Horse.eat()
)。这就是大多数情况下后期绑定的神奇之处

对于不可能被重写的实例方法,编译器执行与静态方法类似的操作,并生成直接调用该方法的指令


(以上内容在技术上并不准确,只是一个概念性的说明,让您了解发生了什么)

私有方法不能被覆盖


这里你可能忽略了一件事:你的主要方法是在动物类中。因此,从同一个类调用私有方法eat()没有问题。如果将main方法移动到另一个类中,您会发现对动物调用eat()将导致编译器错误

当然,如果您将@Override注释放在Horse中的eat()上,您也会收到一个编译器错误。因为,正如其他人很好地解释的那样:您并没有覆盖示例中的任何内容

因此,本质上:

  • 你没有凌驾于任何事情之上
  • 您没有调用您认为正在调用的方法
  • 最后,关于你的评论:当然有一个动物对象。马是延伸的动物;所以任何马的物体也是动物的物体。这就是为什么你能写下来

    Animal a = new Horse();
    

    但需要理解的重要一点是:在这一行之后,编译器不再知道“a”实际上是一匹马。你宣布“a”为动物;因此,编译器允许您调用Animal声明的方法。请记住:继承基本上是关于描述“是-是”关系的:在您的示例中,马是动物。

    简而言之,您在Java:-)中重载了“重写”的预期含义

    让我们假设其他人编写了
    Animal
    类:(稍微重写它,不改变任何语义,只是为了演示一个好的实践)。我们还将假设
    Animal
    compi
    Animal a = new Horse();
    
    public class Animal {
    
        public static void main(String args[]) {
    
            Animal a = new Animal(); // yes, Animal, no Horse yet.
            a.eat();
        }
        ///// Animal's private methods, you should not look here
        private void eat() {
            System.out.println("animal eating");
        }
        ///// Animal's private methods, you should not look here
    }
    
    class Horse extends Animal {
        @Override
        public void eat() {
            System.out.println("Horse eating");
        }
    }
    
    Error:(21, 5) java: method does not override or implement a 
    method from a supertype
    
    Animal a = new Horse();