Pra*_*kar 27 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();
}
}
class Horse extends Animal {
public void eat() {
System.out.println("Horse eating");
}
}
Run Code Online (Sandbox Code Playgroud)
该程序输出如下.
动物吃
这就是我所知道的:
private void eat()方法,它不一定会在子类中访问,因此这里不会出现方法覆盖的问题,因为JLS明确定义了它.public void eat()从Horse类调用方法Animal a = new Horse();因多态性而有效.为什么a.eat()要从Animal类中调用方法?我们正在创建一个Horse对象,为什么要调用Animal类的方法呢?
tem*_*def 24
标记的private方法不能在子类中重写,因为它们对子类不可见.从某种意义上说,你Horse根本就不知道任何这Animal有一个eat方法,因为它被标记private.其结果是,Java不考虑Horse的eat方法是一种替代.这主要是作为安全功能设计的.如果一个类有一个标记的方法private,那么假设该方法应该只用于类内部,并且外部世界完全无法访问它.如果子类可以覆盖private方法,那么它可能以意外的方式改变超类的行为,这是(1)不期望的和(2)潜在的安全风险.
因为Java假定private不会覆盖类的方法,所以每当private通过某种类型的引用调用方法时,Java将始终使用引用的类型来确定要调用的方法,而不是使用类型的该引用指向的对象确定要调用的方法.这里,引用是类型的Animal,所以这是被调用的方法,即使该引用指向a Horse.
Adr*_*hum 14
我不确定我是否理解你的困惑.根据您的了解:
你是对的,Horse.eat()不是压倒一切Animal.eat()(因为它是私人的).换句话说,当你打电话时anAnimal.eat(),没有后期绑定发生,因此,你只是在呼唤Animal.eat(),这就是你所看到的.
从你的其他评论来看,似乎你的困惑是编译器决定调用什么.这是一个非常高级的解释:
当编译器看到时 Animal a =...; a.eat();,它将尝试解析要调用的内容.
例如,如果它看到的eat()是一个静态方法,给定a是一个引用Animal,编译器会将其转换为调用Animal.eat().
如果它是一个实例方法,并且它遇到了一个可能被子类覆盖的方法,那么编译器会做的是,它不会生成调用特定方法的指令.相反,它将生成指令以从vtable执行某种查找.从概念上讲,每个对象都有一个小表,其中键是方法签名,值是对实际调用方法的引用.例如,如果在你的情况下,Animal.eat()不是私人的,那么Horse的vtable将包含的是什么["eat()" -> "Horse.eat()"].因此,在运行时,给定一个Animal引用并被eat()调用,实际发生的是:从引用对象的vtable中查找eat(),并调用相关的方法.(如果ref指向a Horse,则关联的方法将是Horse.eat()).这就是大多数情况下后期绑定的魔力.
使用无法覆盖的实例方法,编译器会执行与静态方法类似的操作,并生成直接调用该方法的指令.
(以上在技术上并不准确,只是一个概念性的例子,让你了解发生了什么)
您可能在这里忽略的事情:您的主要方法是在Animal类中.因此从同一个类调用private方法eat()是没有问题的.如果将main方法移动到另一个类中,您会发现在Animal上调用eat()会导致编译错误!
当然:如果你在Horse中的eat()上放了@Override注释,你也会收到编译器错误.因为,正如其他人已经很好地解释过的那样:在你的例子中,你并没有覆盖任何东西.
所以,实质上:
最后,关于你的评论:当然有一个Animal对象.马正在扩展动物; 所以任何Horse-object也是Animal对象.这就是你能够写下来的原因
Animal a = new Horse();
Run Code Online (Sandbox Code Playgroud)
但重要的是要理解:在那一行之后,编译器不再知道"a" 实际上是一匹马.你宣称"a"为动物; 因此编译器允许您调用Animal声明的方法.请记住:继承基本上是描述"IS-A"关系:在您的示例中,马是动物.
| 归档时间: |
|
| 查看次数: |
2290 次 |
| 最近记录: |