欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

java三大特性之多态性和面试题分析

程序员文章站 2022-07-10 18:32:17
java三大特性之多态性,基础知识归纳整理和面试分析解读...



一、多态性的理解

1.1 多态性的体现和理解

1.1.1 多态的体现

Java中的体现:对象的多态性:父类的引用指向子类的对象(或子类的对象赋给父类的引用)
可以直接应用在抽象类和接口上。

1.1.2 多态的作用

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

1.1.3 多态实现的前提

  1. 需要存在类的继承或者实现关系;
  2. 有方法的重写。

1.1.4 区分实现方法与成员方法

实现方法:一个父类有多个子类,在构建方法时以父类的引用作为形参,在调用方法时,传入不同子类对象实参。
成员方法:编译时:要查看引用变量所声明的类中是否有所调用的方法。运行时:调用实际new的对象所属的类中的重写方法。

1.2 变量的角度看多态性

1.2.1 成员变量与引用变量

成员变量: 不具备多态性,只看引用变量所声明的类。
引用变量
Java引用变量有2个类型: 编译时类型 和 运行时类型。

  1. 编译时类型由声明该变量时使用的类型决定;
  2. 运行时类型由实际赋给该变量的对象决定。
    简称:编译时,看左边;运行时,看右边。
### 1.2.2 注意 
  1. 对象的多态性,只适用于方法,不适用于属性(编译和运行都看左边)
  2. 若编译时类型和运行时类型不一致,就出现了对象的多态性 (Polymorphism)
  3. 多态情况下 ,“ 看左边 ” :看的是父类的引用(父类中不具备子类特有的方法) “ 看右边 ” :看的是子类的对象(实际运行的是子类重写父类的方法)

1.3 对象的多态性

1.3.1 对象的多态性体现

体现:在Java中,子类的对象可以替代父类的对象使用
前提

  1. 一个变量只能有一种确定的数据类型
  2. 一个引用类型变量可能指向(引用)多种不同类型的对象
    子类可看做是特殊的父类 ,所以父类类型的引用可以指向子类的对象:向上转型(upcasting)。
    例:Person p = new Student();
    Object o = new Person(); //Object类型的变量o,指向Person类型的对象
    o = new Student(); //Object类型的变量o,指向Student类型的对象
  3. 一个引用类型变量如果声明为父类的类型,但实际引用的是子类对象,那么该变量就不能再访问子类中添加的属性和方法。
  4. 方法声明的形参类型为 父类 类型,可以使用子类的对象 作为实参调用该方法。

1.3.2 子类继承父类

  1. 若子类重写了父类方法,就意味着子类里定义的方法彻底覆盖了父类里的同名方法,系统将不可能把父类里的方法转移到子类中。
  2. 对于实例变量则不存在这样的现象,即使子类里定义了与父类完全相同的实例变量,这个实例变量依然不可能覆盖父类中定义的实例变量

1.4 多态的使用

虚拟方法调用(Virtual Method Invocation)

1.4.1 虚拟方法定义

定义:子类中定义了与父类同名同参数的方法,在多态情况下,将此时父类的方法称为虚拟方法;

1.4.2 虚拟方法的调用

调用
当调用子父类同名同参数的方法时,实际执行的是子类重写父类的方法
父类根据赋给它的不同子类对象,动态调用属于子类的该方法。这样的方法调用在编译期是无法确定的。
例: Person e = new Student();
e.getInfo(); // 调用Student 类的getInfo()
注意:(动态绑定)区分编译时类型和运行时类型;编译时e为Person类型,而方法的调用是在运行时确定的,所以调用的是Student类的getInfo()方法。
总结
多态情况下 ,
“ 看左边 ” :看的是父类的引用(父类中不具备子类特有的方法)
“ 看右边 ” :看的是子类的对象(实际运行的是子类重写父类的方法)

1.5 对象类型转换 (Casting)

1.5.1 基本数据类型的转换

  1. 自动类型转换:小的数据类型可以自动转换成大的数据类型
  2. 强制类型转换:可以把大的数据类型强制转换(casting)成小的数据类型
    如 float f=(float)12.0; int a=(int)1200L

1.5.2 多态情况下类型转换

对Java对象的强制类型转换称为 造型(向下转型)

  1. 向上转型:(从子类到父类的类型转换)自动进行,通过多态
  2. 向下转型:(从父类到子类的类型转换)必须通过造型(强制类型转换)实现,使用之前需要用 instanceof 进行条件判断。
  3. 无继承关系的引用类型间的转换是非法的
    注意:使用向下转型,容易出现异常:ClassCastException; 因此需要用 instanceof 进行关系确认。

1.5.3 instanceof 操作符

格式:x instanceof A 检验 x 是否为类 A 的对象,返回值为boolean型。
x是变量名,A是类型。
注意

  1. 要求x所属的类与类A必须是子类和父类的关系,否则编译错误。
  2. x instanceof A 返回值为true;类B为类A的父类,则 x instanceof B 返回值也是true;
  3. instanceof 运算符判定父类引用指向子类实例对象的具体类型。

二、面试经典例题

2.1 方法的重载与重写区分

  1. 从定义上区分:
  2. 从编译和运行的角度看:
    重载,是指允许存在多个同名方法,而这些方法的参数不同。编译器根据方法不同的参数表,对同名方法的名称做修饰。
    对于编译器而言,这些同名方法就成了不同的方法。它们的调用地址在编译期就绑定了。
    Java的重载是可以包括父类和子类的,即子类可以重载父类的同名不同参数的方法。
    所以对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法,这称为“早绑定”或“静态绑定”;
    重写,(多态)只有等到方法调用的那一刻,解释运行器才会确定所要调用的具体方法,这称为“晚绑定”或“动态绑定”。
    引用一句Bruce Eckel的话:“不要犯傻,如果它不是晚绑定,它就不是多态。”

代码展示

// 虚方法调用测试 class PersonPy { String name; int age; int id = 1001; public void eatpy(){ System.out.println("人:吃饭"); } public void walkpy(){ System.out.println("人:走路"); } } class Man extends PersonPy{ boolean isSmoking; int id = 1002; public void earnMoney(){ System.out.println("男人负责挣钱养家"); } public void eat(){ System.out.println("男人多吃肉,长肌肉"); } public void walk(){ System.out.println("男人霸气的走路"); } } class Woman extends PersonPy{ boolean isBeauty; public void goShopping(){ System.out.println("女人喜欢购物"); } public void eat(){ System.out.println("女人少吃,为了减肥"); } public void walk(){ System.out.println("女人窈窕的走路"); } } class PersonPyTest { public static void main(String[] args) { PersonPy p1 = new PersonPy(); p1.eatpy(); Man man = new Man(); man.eat(); man.age = 25; man.earnMoney(); System.out.println("*******************"); //对象的多态性:父类的引用指向子类的对象 PersonPy p2 = new Man(); //Person p3 = new Woman(); //多态的使用:当调用子父类同名同参数的方法时,实际执行的是子类重写父类的方法 ---虚拟方法调用 p2.eatpy(); p2.walkpy(); //		p2.earnMoney(); System.out.println(p2.id); //1001 System.out.println("****************************"); //不能调用子类所特有的方法、属性:编译时,p2是Person类型。 p2.name = "Tom"; //		p2.earnMoney(); //		p2.isSmoking = true; //有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型,导致 //编译时,只能调用父类中声明的属性和方法。子类特有的属性和方法不能调用。 //如何才能调用子类特有的属性和方法? //向下转型:使用强制类型转换符。 Man m1 = (Man)p2; m1.earnMoney(); m1.isSmoking = true; //使用强转时,可能出现ClassCastException的异常。 //		Woman w1 = (Woman)p2; //		w1.goShopping(); if(p2 instanceof Woman){ Woman w1 = (Woman)p2; w1.goShopping(); System.out.println("******Woman******"); } if(p2 instanceof Man){ Man m2 = (Man)p2; m2.earnMoney(); System.out.println("******Man******"); } if(p2 instanceof PersonPy){ System.out.println("******Person******"); } if(p2 instanceof Object){ System.out.println("******Object******"); } //练习: //问题一:编译时通过,运行时不通过 //举例一: //		Person p3 = new Woman(); //		Man m3 = (Man)p3; //举例二: //		Person p4 = new Person(); //		Man m4 = (Man)p4; //问题二:编译通过,运行时也通过 //		Object obj = new Woman(); //		Person p = (Person)obj; //问题三:编译不通过 //		Man m5 = new Woman(); //		String str = new Date(); //		Object o = new Date(); //		String str1 = (String)o; } } 

本文地址:https://blog.csdn.net/Hugh_Guan/article/details/107683866