编译时多态、运行时多态
根据何时确定执行多态方法中的哪一个,多态分为两种情况:编译时多态和运行时多态。如果在编译时能够确定执行多态方法
中的哪一个,称为编译时多态,否则称为运行时多态。
一、编译时多态
方法重载都是编译时多态。根据实际参数的数据类型、个数和次序,Java在编译时能够确定执行重载方法中的哪一个。
方法覆盖表现出两种多态性,当对象引用本类实例时,为编译时多态,否则为运行时多态。例如,以下声明p、m引用本类实例,调用toString()方法是编译时多态。
- public class Test {
- public static void main(String[] args) {
- Person p = new Person(); //对象引用本类实例
- Man m = new Man(); //编译时多态,执行Person类的toString()
- System.out.println(p.toString());
- System.out.println(m.toString()); //编译时多态,执行Man类的toString()
- }
- }
- class Person{
- public String toString() {
- String name = "Person";
- return name;
- }
- }
- class Man extends Person{
- public String toString(){
- String name = "Man";
- return name;
- }
- }
二、运行时多态
1.当以下父类对象p引用子类实例时,p.toString)执行谁的setName()方法?
- Person p = new Man();
- p.toString();
Java支持运行时多态,意为p.toString()实际执行p所引用实例的toString(),究竟执行Person类还是Man类的方法,运行时再确定。如果Man类声明了toString()方法,则执行之;否则执行Person类的toString()方法。
程序运行时,Java从实例所属的类(new 类)开始寻找匹配的方法执行,如果当前类中没有匹配的方法,则沿着继承关系逐层向上,依次在父类或各祖先类中寻找匹配方法,直到Object类。寻找p.toString()匹配执行方法的过程如下图所示。
2.将上述例子中toString方法改为getName,因为在Object类中有toString类,无法测试Person与Man中所匹配的执行方法。
- public class Test { //例子2
- public static void main(String[] args) {
- Person p = new Man();
- System.out.println(((Man) p).getName()); //返回结果为Man
- }
- }
- class Person{}
- class Man extends Person{
- public String getName(){
- String name = "Man";
- return name;
- }
- }
此例中Person类型要引用Man类的实例,因Person中未定义setName()方法,故需要把Person类显式地转换为Man类,然后调用Man中的getName方法。
3.将例子1中Person和Man的方法名改为静态的getName()方法,会返回什么结果呢?
- public class Test { //例子3
- public static void main(String[] args) {
- Person p = new Man();
- System.out.println(p.type); //返回结果为P
- System.out.println(p.getName()); //返回结果为Person
- }
- }
- class Person{
- String type = "P";
- public static String getName() {
- String name = "Person";
- return name;
- }
- }
- class Man extends Person{
- String type = "M";
- public static String getName(){
- String name = "Man";
- return name;
- }
- }
栗子中子类Man隐藏父类Person的属性,而 Person p = new Man() 表示“先声明一个Person类的对象p,然后用Man类对p进行实例化”,即引用类型为Person类,实际代表的是Man类。因此,访问的是Person的属性及静态方法,详细解释如下。
所谓静态,就是在运行时,虚拟机已经认定此方法属于哪个类。“重写”只能适用于实例方法,不能用于静态方法。对于静态方法,只能隐藏,重载,继承。
子类对于父类静态方法的隐藏(hide),子类的静态方法完全体现不了多态,就像子类属性隐藏父类属性一样,在利用引用访问对象的属性或静态方法时,是引用类型决定了实际上访问的是哪个属性,而非当前引用实际代表的是哪个类。因此,子类静态方法不能覆盖父类的静态方法。
父类中属性只能被隐藏,而不能被覆盖;而对于方法来说,方法隐藏只有一种形式,就是父类和子类存在相同的静态方法。
若要转载请注明文章出处:http://blog.csdn.net/why_still_confused https://blog.csdn.net/why_still_confused/article/details/51295707
运行时多态demo:
public class Test {public static void main(String[] args) {//运行时多态Father c = new SonClass();c.method(); //父类的构造方法 子类的构造方法 子类的method()c.method2(); //父类的构造方法 子类的构造方法 父类的method2()}
}class Father {public Father() {System.out.print("父类的构造方法\t");}public void method() {System.out.print("父类的method()\t");}public void method2() {System.out.print("父类的method2()\t");}}class SonClass extends Father {public SonClass() {System.out.print("子类的构造方法\t");}@Overridepublic void method() {System.out.print("子类的method()\t");}}
运行时多态性是面向对象程序设计代码重用的一个最强大机制,动态性的概念也可以被说成“一个接口,多个方法”。Java实现运行时多态性的基础是动态方法调度,它是一种在运行时而不是在编译期调用重载方法的机制,下面就继承和接口实现两方面谈谈java运行时多态性的实现。
一、通过继承中超类对象引用变量引用子类对象来实现
举例说明:
//定义超类superA
class superA
{
int i = 100;
void fun()
{
System.out.println(“This is superA”);
}
}
//定义superA的子类subB
class subB extends superA
{
int m = 1;
void fun()
{
System.out.println(“This is subB”);
}
}
//定义superA的子类subC
class subC extends superA
{
int n = 1;
void fun()
{
System.out.println(“This is subC”);
}
}
class Test
{
public static void main(String[] args)
{
superA a;
subB b = new subB();
subC c = new subC();
a=b;
a.fun(); (1)
a=c;
a.fun(); (2)
}
}
运行结果为:
This is subB
This is subC
上述代码中subB和subC是超类superA的子类,我们在类Test中声明了3个引用变量a, b, c,通过将子类对象引用赋值给超类对象引用变量来实现动态方法调用。也许有人会问:“为什么(1)和(2)不输出:This is superA”。java 的这种机制遵循一个原则:当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法。
所以,不要被上例中(1)和(2)所迷惑,虽然写成a.fun(),但是由于(1)中的a被b赋值,指向了子类subB的一个实例,因而(1)所调用的fun()实际上是子类subB的成员方法fun(),它覆盖了超类superA的成员方法fun();同样(2)调用的是子类subC的成员方法fun()。
另外,如果子类继承的超类是一个抽象类,虽然抽象类不能通过new操作符实例化,但是可以创建抽象类的对象引用指向子类对象,以实现运行时多态性。具体的实现方法同上例。
不过,抽象类的子类必须覆盖实现超类中的所有的抽象方法,否则子类必须被abstract修饰符修饰,当然也就不能被实例化了。
二、通过接口类型变量引用实现接口的类的对象来实现
接口的灵活性就在于“规定一个类必须做什么,而不管你如何做”。我们可以定义一个接口类型的引用变量来引用实现接口的类的实例,当这个引用调用方法时,它会根据实际引用的类的实例来判断具体调用哪个方法,这和上述的超类对象引用访问子类对象的机制相似。
举例说明:
//定义接口InterA
interface InterA
{
void fun();
}
//实现接口InterA的类B
class B implements InterA
{
public void fun()
{
System.out.println(“This is B”);
}
}
//实现接口InterA的类C
class C implements InterA
{
public void fun()
{
System.out.println(“This is C”);
}
}
class Test
{
public static void main(String[] args)
{
InterA a;
a= new B();
a.fun();
a = new C();
a.fun();
}
}
输出结果为:
This is B
This is C
上例中类B和类C是实现接口InterA的两个类,分别实现了接口的方法fun(),通过将类B和类C的实例赋给接口引用a而实现了方法在运行时的动态绑定,充分利用了“一个接口,多个方法”展示了Java的动态多态性。
需要注意的一点是:Java在利用接口变量调用其实现类的对象的方法时,该方法必须已经在接口中被声明,而且在接口的实现类中该实现方法的类型和参数必须与接口中所定义的精确匹配。
结束语:以上就是java运行时多态性的实现方法,大家在编程过程中可以灵活运用,但是在性能要求较高的代码中不提倡运用运行时多态,毕竟Java的运行时动态方法调用较之普通的方法调用的系统开销是比较大的。
编译时多态、运行时多态相关推荐
- java多态编译,java多态 运行时多态和编译时多态
java多态 运行时多态和编译时多态 我们知道java的多态是一个重要的特性,其中体现java的多态有两种形式.运行时的多态和编译时的多态. 编译时的多态会发生在方法重载的时候,方法的重载指方法名相同 ...
- C专家编程 第11章 你懂得C,所以C++不再话下 11.13 多态---运行时绑定
多态---运行时绑定 多态(polymorphism)源于希腊语,意思是"多种形状".在C++中,它的意思是支持相关的对象具有不同的成员函数(但原型相同),并允许对象与适当 ...
- nameof() 到底是编译时还是运行时行为?
咨询区 Gigi: 在 C#6.0 中,可以用 nameof() 直接获取变量或者类型的名字,请问这是一个 编译时 还是 运行时 行为? 回答区 Faris Zacina: 可以肯定的说,它是一种 编 ...
- Qt编译通过,运行时出现the process was ended forcefully问题的解决方案
** Qt编译通过,运行时出现the process was ended forcefully问题的解决方案 ** Debug和Release模式下编译均能通过,调用外部函数也不会提示错误,但是运行就 ...
- c++ 多态 运行时多态和编译时多态_C++学习笔记之多态
多态是面向对象三大特性之一 多态分为两类: 静态多态:函数重载 和 运算符重载 属于静态多态,复用函数名 动态多态:派生类 和 虚函数 实现运行时多态 静态多态和动态多态的区别: 静态多态的函数地址早 ...
- 【QT】QT Creator编译通过,运行时程序异常结束:Crashed
最近有个小项目需要用QT写,奈何没玩过QT,踩了不少坑,这里给大家写一下避避雷. 编译通过,运行异常时报错如下: 这个异常很诡异,什么提示都不给你,网上找了很久,被折磨了快两天了,这里提供给新人一个偷 ...
- 3704对象关闭时_运行时出现了“对象关闭时不允许操作”实时错误3704,请问大家怎么改呢?...
运行时出现了"对象关闭时不允许操作"实时错误3704,请问大家怎么改呢? Option Explicit'这是公共模块 Public adocon As ADODB.Connect ...
- c++ 多态 运行时多态和编译时多态_C++核心编程 第十一节 多态
前言:多态是C++面向对象三大特性之一. 多态,指的是一个类实例的相同方法在不同情形有不同表现形式.具有不同内部结构的对象可以共享外部接口.C++多态就是用一个更通用的基类指针指向不同的子类实例,为了 ...
- 使用std::thread线程相关函数,-static静态编译的程序运行时的一些常见错误
使用std::thread的应用程序,编译时如果是动态链接pthread线程库运行正常,-static静态链接时在某些平台下可能会遇到一些意外错误.如常见编译命令:g++ -std=C++11 tes ...
- QT编译的程序运行时缺少DLL如何处理
编译完QT的程序后,如果在其他地方运行缺少DLL,可以这样做: 在安装有QT5的计算机上,进入QT5的命令行,进入EXE或DLL所在的目录 运行windeployqt filename(这个filen ...
最新文章
- 2017回顾与2018前瞻:机器学习与人工智能
- rust大油田分解机_油田泥浆泵油田环保罐车配套泥浆泵
- ubuntu安装node.js
- 1.0Nvm环境配置
- 这么多Apache顶级项目,SkyWalking为何一枝独秀?
- 不少人暗搓搓的准备春招了,我有一些好东东和招聘信息给你
- 计算机控制z反变换公式,第三章 计算机控制系统的数学描述(修正Z变换).ppt
- i12蓝牙耳机使用说明书图片_飞利浦SHB4385 BASS+无线蓝牙耳机晒单 使用体验
- julia有 pytorch包吗_PyTorch 有哪些坑/bug?
- Java语言程序设计(一)简答题和论述题
- spring 配置版本问题
- javascript是一门多线程的语言_如何理解JavaScript是一种单线程非阻塞脚本语言?...
- MVC实用架构设计(三)——EF-Code First(4):数据查询
- window xp系统安装php环境_在Windows XP下安装Apache+MySQL+PHP环境
- php转换emoji表情为图片输出小程序,微信小程序中使用emoji表情相关
- POJ 3667 Hotel (线段树区间合并)
- NoteExpress文献题录如何导出到excel
- C语言链表的简单的尾插法
- dhcp设置(Padavan dhcp设置)
- 如何为豆瓣FM写一个chrome的歌词插件