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();