[子类有, 父类没有] 编译失败!!!
worker.startWork(); 为什么编译不通过呢? 提示: 找不到符号.
因为引用变量 worker 是 Person 类型, 在 Person 类的方法表中查找方法 startWork(), 找得到吗? 找不到, 更别提常量池解析了. 编译失败.
[子类有, 父类有, 重写, 非静态] 调用子类!!!
worker.say(); 子类重写父类方法, 被重写的方法在子类跟父类的方法表中索引相同.
调用的时候, 在父类类型的方法表中查找 say 方法的索引, 然后把索引存到 PolDemo 类的常量表中(常量池解析, 就是用 Person 类方法表中的索引项来代替常量池中的符号引用).
因为索引相同, 直接拿常量表中 say 方法的索引去子类方法表中调用 say()方法. 所以, 此时调用的是被子类重写的方法. 见图中的内存分配.
[子类有, 父类有, 静态] 调用当前引用类型变量类型中的方法.
因为静态是属于类的, 由实例共享, 所以只看当前引用变量所属的类中的静态方法.
多态中 (父类引用指向子类对象) 成员方法 (非静态) 有以下特点:
编译期根据引用类型变量所属的类型方法表中是否有调用的方法, 没有编译失败.
运行期根据在堆中创建对象的实际类型找到对应的方法表, 从中确定具体的方法在内存中的位置.
堆中实例对象: 子类包含父类, 子类对父类说: 你的就是我的, 我的还是我的.
多态中成员变量的特点分析:
无论编译期还是运行期, 都只参考引用类型变量所属的类中是否有对象的成员变量.
小的总结:
再来看这条语句: Person worker = new Worker(): 多态, 父类引用指向子类对象.
跟这句代码的作用是一样的, Worker w = new Worker(); Person worker = w; 就是子类的引用传给父类类型的引用. 向上转型, 都指向一个实例对象(子类).
1: 为什么可以将子类实例对象的引用传给父类类型引用呢?
答: 多态的体现, 鸽子是鸟类, 鹦鹉是鸟类, 喜鹊是鸟类, 它们都是鸟类, 这就是鸟类的多种表现形态(多态),
它们有鸟类的基本特征与行为, 并且还有自己特有的行为, 那就会把鸟类的基本特征与行为继承过来给自己. extends 关键字声明了他们的关系.
程序中这句话 鸟类 xx 鸟 = new 鸽子(); 就创建了一个鸟类的引用, 它是鸽子.
类的概念是抽象的, 所以它就仅仅是个引用, 只有引用到实例对象, 它才真正实现了自己.
2: 父与子的关系会怎样呢?
由于 Worker 类继承自 Person 类, 所以会先加载 Person 类并初始化成员变量在 Worker 类实例对象中. 这就是子类继承过来的成员变量.
如果子类中定义了与父类相同的成员变量, 变量不会存在重写, 子类实例对象中会有两份相同名称的实例变量, 调用时, 父类的会被隐藏.
如果想调用父类被隐藏的成员变量, 那就要使用 super 关键字.
同样, 子类会继承父类的成员方法作为自己的成员方法, 如果子类定义了与父类相同的成员方法(重写), 多态中会调用子类自己的成员方法.
3: 多态, 又有什么好处呢? 跟这样写 (Worker worker = new Worker();) 有什么区别, 不一样继承父类的属性方法吗?
首先明确: Worker worker = new Worker(); 这样写仅仅是继承关系. 不存在多态特性.
Person worker = new Worker(); 这样写就展示了多态的关系, 鸽子是鸟类的一种表现形态.
有啥好处? 一句话概括: 提高了程序的扩展性, 表现在什么地方呢? 比如:
现在有个工厂, 能解析鸟类的语言, 鸽子是鸟类吧? Ok, 那建一个工厂, 让鸽子说.
- public static void doWork(Dove dove) {
- String sound = dove.say(); // ... 拿到鸽子说的话
- }
鹦鹉是鸟类吧? Ok, 那建一个工厂, 让鹦鹉说.
- public static void doWork(Parrot parrot) {
- String sound = parrot.say(); // ... 拿到鹦鹉说的话
- }
如果还有一千个鸟类要说话, 那我岂不得建一千座工厂?... 累不累? 那既然都是鸟类, 我建一个鸟类的工厂不就行了嘛?
- public static void doWork(Bird bird) {
- String sound = bird.say(); // ... 拿到 xx 鸟说的话...
- }
这时候, 无论你什么鸟进来, 我都让你说话, 而且说得都是你自己的话, 因为你继承我并重写了你的功能.
- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- Worker w = (Worker)worker; // 向下转型. 目的, 为了调用子类非继承父类, 自己特有的方法.
因为多态有个弊端, 只能使用父类的引用访问父类的成员. 所以向下转型是为了访问子类自己的成员.
首先, worker 引用指向的实例对象本来就是子类类型的. 所以赋值给子类类型引用变量非常可以.
然后现在用子类类型的引用就可以访问自己的成员方法了啦啦啦.
来源: http://www.bubuko.com/infodetail-3105933.html