java三大特性面试回答6_java三大特性之多态性和面试题分析

一、多态性的理解

1.1 多态性的体现和理解

1.1.1 多态的体现

Java中的体现:对象的多态性:父类的引用指向子类的对象(或子类的对象赋给父类的引用)

可以直接应用在抽象类和接口上。

1.1.2 多态的作用

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

1.1.3 多态实现的前提

需要存在类的继承或者实现关系;

有方法的重写。

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

实现方法:一个父类有多个子类,在构建方法时以父类的引用作为形参,在调用方法时,传入不同子类对象实参。

成员方法:编译时:要查看引用变量所声明的类中是否有所调用的方法。运行时:调用实际new的对象所属的类中的重写方法。

1.2 变量的角度看多态性

1.2.1 成员变量与引用变量

成员变量: 不具备多态性,只看引用变量所声明的类。

引用变量:

Java引用变量有2个类型: 编译时类型 和 运行时类型。

编译时类型由声明该变量时使用的类型决定;

运行时类型由实际赋给该变量的对象决定。

简称:编译时,看左边;运行时,看右边。

### 1.2.2 注意

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

若编译时类型和运行时类型不一致,就出现了对象的多态性 (Polymorphism)

多态情况下 ,“ 看左边 ” :看的是父类的引用(父类中不具备子类特有的方法) “ 看右边 ” :看的是子类的对象(实际运行的是子类重写父类的方法)

1.3 对象的多态性

1.3.1 对象的多态性体现

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

前提:

一个变量只能有一种确定的数据类型

一个引用类型变量可能指向(引用)多种不同类型的对象

子类可看做是特殊的父类 ,所以父类类型的引用可以指向子类的对象:向上转型(upcasting)。

例:Person p = new Student();

Object o = new Person(); //Object类型的变量o,指向Person类型的对象

o = new Student(); //Object类型的变量o,指向Student类型的对象

一个引用类型变量如果声明为父类的类型,但实际引用的是子类对象,那么该变量就不能再访问子类中添加的属性和方法。

方法声明的形参类型为 父类 类型,可以使用子类的对象 作为实参调用该方法。

1.3.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 基本数据类型的转换

自动类型转换:小的数据类型可以自动转换成大的数据类型

强制类型转换:可以把大的数据类型强制转换(casting)成小的数据类型

如 float f=(float)12.0; int a=(int)1200L

1.5.2 多态情况下类型转换

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

向上转型:(从子类到父类的类型转换)自动进行,通过多态

向下转型:(从父类到子类的类型转换)必须通过造型(强制类型转换)实现,使用之前需要用 instanceof 进行条件判断。

无继承关系的引用类型间的转换是非法的

注意:使用向下转型,容易出现异常:ClassCastException; 因此需要用 instanceof 进行关系确认。

1.5.3 instanceof 操作符

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

x是变量名,A是类型。

注意:

要求x所属的类与类A必须是子类和父类的关系,否则编译错误。

x instanceof A 返回值为true;类B为类A的父类,则 x instanceof B 返回值也是true;

instanceof 运算符判定父类引用指向子类实例对象的具体类型。

二、面试经典例题

2.1 方法的重载与重写区分

从定义上区分:

从编译和运行的角度看:

重载,是指允许存在多个同名方法,而这些方法的参数不同。编译器根据方法不同的参数表,对同名方法的名称做修饰。

对于编译器而言,这些同名方法就成了不同的方法。它们的调用地址在编译期就绑定了。

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

希望与广大网友互动??

点此进行留言吧!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值