多态 (Polymorphism) 大家应该都不陌生,它是我们开发面向对象系统的“老朋友”了 。但是老朋友也会有“烦心”的时候啊,呵呵。有时候不注意,还真会被它难到。譬如下面这个例子,大家可以先不看下面的答案,在自己脑海中运行一道,看看自己想的跟实际结果是否相符。
java 代码
- public class Polymorphism{
- public static void main(String[] args) {
- A b = new B();
- b.fb();
- }
- }
-
- class A {
- public A(){
- }
- public void fa() {
- System.out.println("CLASS A :Function fa Runing......");
- }
-
- public void fb() {
- System.out.println("CLASS A :Function fb Runing......");
- fa();
- System.out.println("CLASS A :Function fb Stop......");
- }
- }
-
- class B extends A {
- public B(){
- }
- public void fa() {
- System.out.println("CLASS B :Function fa Runing......");
- }
-
- public void fb() {
- System.out.println("CLASS B :Function fb Runing......");
- super.fb();
- System.out.println("CLASS B :Function fb Stop......");
- }
- }
下面是它的运行结果:
CLASS B :Function fb Runing......
CLASS A :Function fb Runing......
CLASS B :Function fa Runing......
CLASS A :Function fb Stop......
CLASS B :Function fb Stop......
怎么样,猜对结果了吗?如果结果跟你想象的一模一样,那么恭喜你,你对多态已经有初步了解了,起码在语法层次上是比较熟悉了。但是,千万不要“洋洋得意”,你可否解析结果为什么会是这样吗?我们可以先来梳理一下程序流程:
1、运行main函数,创建B对象,调用B的方法fb,于是打印出"CLASS B :Function fb Runing......",都在情理之中。
2、执行super.fb(),调用父类A的方法fb,首先打印出"CLASS A :Function fb Runing......",预料之中
3、执行方法fa(),打印出"CLASS B :Function fa Runing......",呃?奇怪了,为什么不是执行A的方法fa(),而是子类B中的fa()呢?当前被执行的是类A的方法,那么虚拟机找到的应该是A类的Method Table,找到的应该是A类的方法fa()啊?难解~
4、打印"CLASS A :Function fb Stop......",返回
5、打印"CLASS A :Function fb Stop.....",返回,程序结束。
现在问题清楚了,就是虚拟机在执行类A方法的时候查找的Method Table竟然是子类B的。为什么呢?其实,只要我们清楚java方法调用的方式,这个问题就迎刃而解了。在Java虚拟机中,每启动一个新的线程,虚拟机都会为它分配一个Java栈,而每当线程调用一个java方法时,虚拟机就会在该线程的java栈中压入一个新帧,用以存储参数,局部变量等数据。我们将这个正在被执行的方法称为该线程的当前方法,其相应的栈帧为当前帧。
好了,当我们调用一个方法时,我们需要往当前帧中压入哪些参数呢?简单,方法的参数列表中不是都说得清清楚楚的吗?嗯,对于C语言来说,这个说法是正确的,但是对于诸如C++,Java,Python等面向对象语言来说,却是不对的。大家还记得那个"this"指针吗?!不错,在Java中,所有的实例方法(Instance Method)调用的时候都会把当前对象压入当前帧中,Java虚拟机正是通过这个参数来决定当前所使用的类(通过判断该对象的类型)。
在上面的例子中,main中调用b.fb()时,压入的当前对象自然是B类对象,我们记为b。在B的fb()中调用super.fb()时,压入的就是刚刚压入的对象,也就是b了。同样,在A的fb中调用fa()时,压入的也是b。因此,在使用 invokevirtual指令调用fa()时,找的就是B的方法表(当前对象b的类型为B),也就执行了类B的fa了。
这种现象在构造函数中特别常见,因为构造函数中会隐含使用调用父类的构造函数的,如果在父类的构造函数中调用了实例方法(如 A的fa),而在子类中又覆盖了这个实例方法(如 B的fa),那么得到的结果往往不是我们所要的。因此,我们最好不要在构造函数中使用多态方法,不然,Debug会很痛苦的:)
分享到:
- 2006-12-11 11:27
- 浏览 2468
- 评论(0)
- 论坛回复 / 浏览 (0 / 3697)
- 查看更多
相关推荐
Java语言中的覆盖重载和多态,方法的多态,类型的多态,多态的优点,覆盖(override)识别标志,方法的重载,构造函数的重载,重载的好处,重载与覆盖的比较,编译时多态和运行时多态
这个PPT详细展示了java的多态重载和重写,展示了重载和覆盖的不同点及区别办法
子类可以对从父类继承的方法进行重写,以便根据子类的需要来覆盖掉父类中的方法实现。这样在调用时,可以根据对象的不同来执行对应的方法。 例如: class Animal { public void move() { System.out.println("动物...
重载,重写,覆盖,多态的深刻剖析,具体分析了重载,重写,覆盖,多态之间的区别
Java中的多态主要通过继承、接口和重写实现,子类可以覆盖父类的方法,使得在运行时能够根据实际对象类型调用相应的方法。 多态的应用场景 多态常用于设计模式、事件处理和插件系统等场景,可以提高代码的复用性和可...
内容概要: 本文介绍了Java中方法覆盖(Method Overriding)的概念和用法。读者将学习什么是方法覆盖,为什么需要方法覆盖,以及如何在子类中覆盖父类的方法。此外,还会讨论方法覆盖时的一些注意事项和规则。 能学...
了解多态时的一些规则,如方法覆盖和类型转换等。 阅读建议: 建议读者在阅读前具备Java的基础知识,包括类、对象、方法和继承等概念。 在阅读过程中,尝试编写简单的Java程序,体验多态的效果,并观察父类引用指向...
1继承的概念 2继承的实现 3变量隐藏与方法覆盖 4super关键字 5 子类的构造顺序 6Object类 7 final关键字
Java——多态 多态的特性 1.可替换性(substitutability)。多态对已存在代码具有可替换性。 例如,多态对圆Circle类工作,对其他任何圆形几何体,如圆环, 也同样工作。 2.可扩充性(extensibility)。多态对代码...
继承和多态的相关Java源码下载,比较散,像覆盖和重载、对象类Object和它的toString()方法、多态性、动态绑定和一般程序设计、数组线性表ArrayList类、final类、方法和变量、数据域和静态方法的隐藏、调用父类的方法...
13.java学习第十三章——方法覆盖和多态.pdf
Java基础精品课11-多态录屏4.静态方法不能被覆盖.mp4
1、实验题目:多态和接口编程 2、实验目的:掌握重载、覆盖的多态Java编程,理解类的继承和接口实现的内涵,掌握向上转型的编程技术。
设计一个类Shape(图形)包含求面积和周长的area()方法和perimeter()方法以及设置颜色的方法SetColor(),并利用Java多态技术设计其子类Circle(圆形)类、Rectangle(矩形)类和Triangle(三角形)类,并分别实现...
Java中的equals方法和hashCode方法有什么关系? Java中什么是重载【Overloading】?什么是覆盖【Overriding】?它们有什么区别? Java中什么是多态?如何实现多态? Java中什么是接口?它有什么作用? Java中什么是...
invokeMethod.java 同一个类中调用方法示例 invokeOther.java 类的外部调用方法示例 invokeStaticMethod.java 调用静态方法示例 localVariable.java 演示局部变量 localVSmember.java 局部变量与成员变量同名...
java---- 封装,接口,继承,覆盖,构造过程,多态,static、this、super、final用法
Java,C#,C++在继承,覆盖和多态,抽象类等几个方面的比较归纳..........................
3.3.3 重写覆盖问题;3.3.4 学生实践练习;3.3.4 学生实践练习;3.4 多态;3.4 多态;3.4.1 多态应用;3.4.1 多态应用;3.4.2 引用变量的强制类型转换 ;3.4.2 引用变量的强制类型转换 ;3.4.3 instance of运算符 ;3.4.4 学生...
java八股文面试总结-合集在文档 什么是Java中的HashMap?它的工作原理是什么?...在Java中,多态可以通过继承和重写(覆盖)实现。当子类重写父类的方法时,调用该方法时将根据实际对象的类型来确定调用哪个方法。