从零开始的Java编程之多态详解

it2023-09-01  69

一、多态的定义:

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就能访问子类的专属方法和变量了
最新回复(0)