从零开始的Java编程之多态详解
一、多态的定义:
1.一个对象变量可以指示多种数据类型的现象称为多态,是同一种行为具有不同的表现现象的能力。
2.在运行时能自动的选择调用那个方法的现象称为动态绑定。
二、多态的三个条件以及优缺点:
-
三个条件
- 要有继承关系。
- 子类要重写父类的方法。
- 父类引用指向子类对象。
-
优点
- 消除类型之间的耦合关系。
- 可替换性
- 可扩充性
- 接口性
- 灵活性
- 简化性
-
缺点
无法直接访问子类特有成员
三、案例
- 父类
class Employee{
String name = "员工";
static int age = 25;
public void work(){
System.out.print("员工的工作");
}
public static void sleep(){
System.out.print("员工睡觉了");
}
public void holiday(){
System.out.print("员工放假了");
}
}
-
子类
class Programmer extends Employee{//子类继承父类 String name = "程序员"; static int age = 90; String myname = "小明"; public void work(){ //重写方法 System.out.print("小明在敲代码"); } public static void sleep(){ System.out.print("小明在加班"); } public void noholiday(){ //子类特有的属性 System.out.print("小明在公司度假") } }
-
测试类
class Test{ public static void main(String[] args){ Employee p = new Programmer(); //父类的引用指向子类的对象 p.work(); p.sleep; p.holiday; //以下两个方法的调用就是多态的缺点,无法直接访问子类的特有成员 //p.noholiday(); //System.out.print(p.myname); System.out.println(p.name); System.out.println(p.age); } }
-
结果
1.分析结果:
假设调用x.f(param),隐式参数声明为B类的对象(参考上述代码的子类与父类的关系)
-
编译器查看对象的声明类型的方法名
有可能存在多个名为f的方法,但参数的类型不一样,如f(int)和f(String),编译器会一一列举所有B类中名为f的方法和其超类A中可访问属性且名为f的方法
至此编译器获得所有可能被调用的候选方法
-
接下来查看调用方法时提供的参数类型
如果所有候选方法中存在一个与提供的参数类型完全匹配,就选择这个方法。这个过程被称为重载解析。例如,对于调用x.f(“Hello”)来说,编译器将会挑选 f(String) 而不是 f(int)。由于允许类型转换(int转换为double,Programmer可以转换成Employee等等),所有的过程很复杂。如果编译器没有找到与参数类型匹配的方法,或者转换后有多个方法与之可以匹配就会报错。
至此编译器已获得需要调用的方法名字和参数类型。
-
根据访问修饰符选择绑定方式
如果是
private
,static
,final
方法或者构造器,那么编译器将可以准确的知道应该调用那个方法,我们将这种调用方式称为 静态绑定 。与之对应的时调用的方法依赖x的实际类型,并且在运行中实现的 动态绑定 。上面的例子中,P的实际类型为Programmer ,编译器采用动态方法的方式生成一条调用方法的指令。 -
程序运行,且采用动态绑定调用方法时
虚拟机一定调用与x所引用对象的实际类型最合适的那个方法,假设x的类型是B,B是A的子类,如果B定义的方法f(String),就直接调用它,否则就在B的超类中寻找f(String) ,以此类推。
每次调用方法都要进行搜索,时间开销相当的大,因此虚拟机预先为每一个类创建一个方法表,其中列出所有方法的签名(签名是方法的名字与参数列表) 与实际调用的方法,在调用方法时,虚拟机仅查询这个表就行了。
三、调用总结:
-
那么我们可以根据以上的情况总结出多态成员的访问特点了
-
成员变量
编译看左边(父类) 运行时看左边(父类)
-
成员方法
编译看左边(父类) 运行时看右边(子类)
-
静态方法
编译看左边(父类) 运行时看左边(父类)
-
四、类型转换
-
向上转型
Employee p = new Programme();
子类对象Programme转化为父类对象Employee,向上转型时,这个时候Programme这个引用调用的方法是子类方法。子类单独定义的方法会丢失(nohliday)。
子类引用不能指向父类对象。Programme p = (Programme) new Employee(); 是会报错的。
向上转型的好处:
- 减少重复代码。
- 是代码变得整洁。
- 提高系统的扩展性。
-
向下转型(使用场景:当需要使用子类的特殊方法时)
//向下转型 Employee e = new Programme(); Programme p = null; p = (Programme) e //也可简写成 Programme p = (Programme) e; 这个时候p就能访问子类的专属方法和变量了
推荐阅读
-
详解Java多态的概念、注意事项和用法
-
从零开始的Java编程之多态详解
-
c++的多态详解(附与java的异同)
-
Java笔记之多态的详解
-
面向对象的三大特征之“多态”详解
-
重温java知识(二十、面向对象多态性之二:对象的多态性之对象向上转型)
-
【SpringBoot动态加载Spring容器的类】 博客分类: 编程语言--JAVA之SpringSpring-Boot
-
java 注解的几大作用及使用方法详解(完) 博客分类: 编程语言 注解 java 注解
-
java 注解的几大作用及使用方法详解(完) 博客分类: 编程语言 注解 java 注解
-
Java语法基础之选择结构的if语句、switch语句详解