Java理解误区——方法的重载是多态性的一种体现?

  • Post author:
  • Post category:java




理解误区


面试题 :


“方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。重载发生在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同或者二者都不同)则视为重载;重写发生在子类与父类之间,重写要求子类被重写方法与父类被重写方法有相同的返回类型,比父类被重写方法更好访问,不能比父类被重写方法声明更多的异常(里氏代换原则)。重载对返回类型没有特殊的要求。”


方法的重载是多态性的一种体现吗?

其实这是一个误区,让我很长的一段时间都理解错了,直到又一次系统复习Java的时候才理解,方法的重载并不是是多态性的一种体现。

下面具体谈谈方法的重载和多态性。



一. 方法重载



定义

在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或者参数类型不同即可。



特点

与返回值类型无关,只看参数列表,且参数列表必须不同。 (参数个数或参数类型)。调用时,根据方法参数列表的不同来区别。


“两同一不同”:

  • 同一个类、相同方法名
  • 参数列表不同:参数个数不同,参数类型不同

注:

判断是否为重载,跟方法的权限修饰符、返回值类型、形参变量名、方法体都没有关系!



示例

//返回两个整数的和
int add(int x,int y){return x+y;}

//返回三个整数的和
int add(int x,int y,int z){return x+y+z;}

//返回两个小数的和
double add(double x,double y){return x+y;}



二. Java 中多态的定义

多态是指允许不同类的对象对同一消息做出响应。即同一消息可以根据发送对象的不同而采用多种不同的行为方式(发送消息就是函数调用)。继承在为多态的实现做了准备。子类继承父类,我们可以编写一个指向子类的父类类型引用,该引用既可以处理父类对象,也可以处理子类对象,当相同的消息发送给子类或者父类对象时,该对象就会根据自己所属的引用而执行不同的行为,这就是多态。即多态性就是相同的消息使得不同的类做出不同的响应。

简单来说就是可以理解为一个事物的多种形态。



Java 实现多态的必要条件

Java实现多态有三个必要条件 :

继承,方法重写,向上转型


继承

: 在多态中必须存在有继承关系的子类和父类。


方法重写

: 子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法。


向上转型

: 在多态中需要将子类的引用赋给父类对象,只有这样该引用才能够具备技能调用父类的方法和子类的方法。

简单来说就是:

父类引用指向子类对象

(或子类的对象赋给父类的引用)。



多态的作用

提高了代码的通用性,常称作接口重用。



多态的使用

有了对象的多态性以后,我们在编译期,只能调用父类中声明的方法,但在运行期,我们实际执行的是子类重写父类的方法。

总结:

编译,看左边;运行,看右边。

  • Java引用变量有两个类型: 编译时类型和运行时类型。 编译时类型由声明该变量时使用的类型决定, 运行时类型由实际赋给该变量的对象决定。

    简称: 编译时, 看左边;运行时, 看右边。
  • 若编译时类型和运行时类型不一致, 就出现了对象的多态性(Polymorphism)。
  • 多态情况下, “看左边” : 看的是父类的引用(父类中不具备子类特有的方法);“看右边” :看的是子类的对象(实际运行的是子类重写父类的方法)。
  • 对象的多态 —在Java中,子类的对象可以替代父类的对象使用,一个变量只能有一种确定的数据类型, 而一个引用类型变量可能指向(引用)多种不同类型的对象。
Person p = new Student();
Object o = new Person();//Object类型的变量o, 指向Person类型的对象
o = new Student(); //Object类型的变量o, 指向Student类型的对象
  • 子类可看做是特殊的父类, 所以父类类型的引用可以指向子类的对象:向上转型(upcasting)。
  • 一个引用类型变量如果声明为父类的类型,但实际引用的是子类对象,那么该变量就不能再访问子类中添加的属性和方法, 属性是在编译时确定的,编译时e为Person类型,没有school成员变量,因而编译错误。
Student m = new Student();
m.school = “pku”; //合法,Student类有school成员变量
Person e = new Student();
e.school = “pku”; //非法,Person类没有school成员变量



总结


1. 对象的多态性,只适用于方法,不适用于属性(编译和运行都看左边)。


2.多态使用前提: ① 类的继承或者实现关系 ② 方法的重写 ③向上转型。



三. 虚拟方法调用(Virtual Method Invocation)



正常方法调用 :

Person e = new Person();
e.getInfo();
Student e = new Student();
e.getInfo();



虚拟方法调用(多态情况下)

子类中定义了与父类同名同参数的方法,在多态情况下,将此时父类的方法称为虚拟方法,父类根据赋给它的不同子类对象,动态调用属于子类的该方法。这样的方法调用在编译期是无法确定的。

Person e = new Student();
e.getInfo(); //调用Student类的getInfo()方法



编译时类型和运行时类型

编译时e为Person类型,而方法的调用是在运行时确定的,所以调用的是Student类的getInfo()方法。 ——动态绑定



四. 方法的重载、重写与多态性(方法的重载是多态性的一种体现?NO)


从编译和运行的角度看:

重载,是指允许存在多个同名方法,而这些方法的参数不同。 编译器根据方法不同的参数表, 对同名方法的名称做修饰。对于编译器而言,这些同名方法就成了不同的方法。 它们的调用地址在编译期就绑定了。 Java的重载是可以包括父类和子类的,即子类可以重载父类的同名不同参数的方法。

所以:对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法,这称为“早绑定”或“静态绑定” ;

而对于多态,只有等到方法调用的那一刻, 解释运行器才会确定所要调用的具体方法,这称为“晚绑定”或“动态绑定” 。

引用一句Bruce Eckel的话: “不要犯傻,如果它不是晚绑定, 它就不是多态。”

所以说,方法的重载并不是多态性的一种体现。


注: 如果脱离Java语言范畴,在广义的计算机语言范畴里来说,方法重载毫无疑问是一种多态,同一个方法名,根据不同的传参执行不同的行为句号