面试

您所在的位置:网站首页 java中的多态性 面试

面试

2024-06-03 15:01:24| 来源: 网络整理| 查看: 265

1 多态的含义及作用

在面向对象(OOP)的程序设计语言中,多态与封装、继承合称为OOP的三大特性。 封装,说简单点就是合并属性和行为创建一种新的数据类型,而继承就是建立数据类型之间的某种关系(is-a),而多态就是这种关系在实际场景的运用。 简单点说,多态就是把做什么和怎么做分开了;其中,做什么是指调用的哪个方法,我是去吃饭(方法a)还是去睡觉(方法b),怎么做是指实现方案,如果我选择吃饭,那么我是吃米饭还是吃面条,”分开了“则是指两件事不在同一时间确定。 说的学术点,多态就是父类的引用指向子类的对象。这样做的好处就是可以消除类型之间的耦合关系。可以看下面的实例:

public class Test1 { public static void main(String[] args) { method(new Circle()); } static void method(Shape shape) { shape.draw(); } } class Shape { public void draw() {}; public void erase() {} } class Circle extends Shape { public void draw() { System.out.println("draw circle"); } public void erase() { System.out.println("erase circle"); } } class Square extends Shape { public void draw() { System.out.println("draw square"); } public void erase() { System.out.println("erase square"); } } class Triangle extends Shape { public void draw() { System.out.println("draw triangle"); } public void erase() { System.out.println("erase triangle"); } }

这里通过类Circle,类Square和类Triangle继承类Shape,使得可以在Test1提供统一的访问入口,即方法method();如果没有继承和多态,不仅代码冗余而且类(Circle/Square/Triangle)与类(Test1)之间有耦合。

那么为什么可以通过继承实现这样的功能呢?这就要讨论多态的原理了。

2 多态的分类及原理

上面的实例中,Shape类是父类,其他三个类是子类;method()方法接收的参数是父类Shape的对象,但调用的时候却传递子类Circle的对象,这就是奇怪之处,也是多态的体现。 这里我们提出一个疑问,method(Shape shape)接受一个Shape引用,那么在这种情况下,编译器怎么样才能知道这个Shape引用指向的是Circle对象,而不是Square对象或Triangle对象呢? 答案是:编译器也不知道。既然编译器不知道,那么肯定就是在代码运行时确定的,具体表现就是确定方法调用以及调用方法对象之间的关系,那么这个关系的确定就称为绑定。由于绑定动作发生在运行时,所以多态也称运行时绑定或者后期绑定,相比于静态的编译期,多态也称为动态绑定。

动态绑定很抽象,我们不禁要问:JVM是如何绑定的呢?

关于这个问题,TIJ里面是这么说的,如果一种语言想实现后期绑定,就必须具有某种机制,以便在运行时能判断对象的类型,从而调用恰当的方法。也就是说,编译器不知道对象的类型,但是方法调用机制能够找到正确的方法体,并加以调用。后期绑定机制随编程语言的不同而有所不同,但是只要想一下就会得知,不管怎么样都必须在对象中安置某种”类型信息“。 所以,动态绑定的过程可以理解为下面几个过程:

1 当虚拟机创建Circle类对象的时候,会创建一个类的方法列表,同时包含父类的方法列表。2 子类型circle对象的引用向上转换成shape引用,确定引用与对象之间的关系。3.虚拟机会找到参数引用实际指向的地址,也就是shape引用实际指向circle对象,并查询对象的方法列表,如果在circle对象中找到这个方法,就直接调用,否则查询父类shape方法并调用。

注意步骤2中发生了类型转换。我们知道基本数据类型可以发生类型转换,如byte,short,char和int之间;那么引用类型之间也可以,但需要满足一定的条件(有继承关系)。那么这里发生的转换是从circle对象转到shape对象,即从子类转换父类,是向上转换。

3 多态无处不在

Java中除了static方法和final方法(private方法属于final方法)是前期绑定,其他所有方法都是后期绑定。

①final关键字修饰的方法不能被重写,即防止被其他人覆盖该方法,同时也能避免动态绑定。 public class Test1 { public static void main(String[] args) { method(new Circle()); } static void method(Shape shape) { shape.change(); } } class Shape { public void draw() {}; public void erase() {} public final void change() { System.out.println("shape change"); } } class Circle extends Shape { public void draw() { System.out.println("draw circle"); } public void erase() { System.out.println("erase circle"); } }

由于final方法不能被重写,所以直接调用父类的change()方法。

②private关键字修饰的方法对子类不可见,也就不能被子类继承,那么也就无法通过子类对象调用private级别的方法;唯一调用的途径就是在类内通过其对象本身。所以说,private级别的方法和定义这个方法的类绑定在一起了。 ③static关键字修饰的方法属于类而非对象,所以不能被重写,但static方法可以被子类继承(不能表现出多态性,因为父类的静态方法会被子类覆盖)。 public class Test1 { public static void main(String[] args) { new Circle().change(); // ① shape change new Square().change(); // ② square change method(new Circle()); // ③ shape change method(new Square()); // ④ shape change } static void method(Shape shape) { shape.change(); } } class Shape { public static void change() { System.out.println("shape change"); } } class Circle extends Shape {} class Square extends Shape { public static void change() { System.out.println("square change"); } }

从上面的运行结果可以得出:

类Circle没有重写change()方法,但①表明Circle类继承了Shape类的change()方法,所以static方法可以被子类继承。类Square”重写“了父类change()方法,结果②表明square对象调用的是自身对象的change()方法;注意这里的”重写“加了引号。结果③和结果④表明,当子类向上转换成父类时,不论子类中有没有定义该静态方法,引用都会调用父类的静态方法。如果子类square真的重写父类change()方法,那么结果④就不对了,所以这里的重写只是表面的重写,而不是真正意义上的重写,即static方法并不能被子类重写。(可以尝试在子类的change()方法加上@Override注解)结果④进一步表明,子类的change()方法被父类shape隐藏了。可以简单理解为,调用该引用类型内的static方法。 4 多态的缺陷 ①类的属性没有多态性

除了特别注意private级别的方法不能被继承以及静态方法可以继承但不能表现多态性外,还要注意类的属性也不能表现多态性,但是可以被继承。

public class Test1 { public static void main(String[] args) { Shape shape = new Circle(); System.out.println("shape.radius: " + shape.radius + " , shape.getRadius: " + shape.getRadius()); System.out.println("shape.area: " + shape.area + " , shape.getArea: " + shape.getArea()); Circle circle = new Circle(); System.out.println("circle.radius: " + circle.radius + " , circle.getRadius: " + circle.getRadius() + " , circle.getSuperRadius: " + circle.getSuperRadium()); System.out.println("circle.area: " + circle.area + " , circle.getArea: " + circle.getArea()); } } class Shape { public int radius = 0; public double area = 0.0; public int getRadius() { return radius; } public double getArea() { return area; } } class Circle extends Shape { public int radius = 1; public int getRadius() { return radius; } public double getArea() { return area; } public int getSuperRadium() { return super.radius; } }

运行结果:

shape.radius: 0 , shape.getRadius: 1 shape.area: 0.0 , shape.getArea: 0.0 circle.radius: 1 , circle.getRadius: 1 , circle.getSuperRadius: 0 circle.area: 0.0 , circle.getArea: 0.0

根据结果可以知道,属性area被子类继承,属性radius被覆盖,所以属性域可以被子类继承,但不具备多态性。对于属性area,子类和父类共同指向同一块内存空间,而对于属性radius,子类和父类独自拥有存储字符串的内存空间,所以radius属性根据引用类型来决定。

②构造函数没有多态性

构造函数实际上是static方法,所以构造函数不具备多态性。但我们知道子类的每次实例化,父类都会默认执行不带参数的构造方法;这样做的目的是为了让父类自己初始化自个的属性(一般为private)从而构建完整的父类对象共子类使用。 但如果父类构造函数中调用被子类重写的实例方法,会出现什么结果呢?看下面的实例:

public class Test1 { public static void main(String[] args) { new Circle(5); } } class Shape { public Shape() { System.out.println("shape() before draw()"); draw(); System.out.println("shape() after draw()"); } public void draw() { System.out.println("shape draw()"); } } class Circle extends Shape { private int radius = 1; public Circle(int radius) { this.radius = radius; System.out.println("circle.circle(). radius = " + radius); } public void draw() { System.out.println("Circle.draw(). radius = " + radius); } }

运行结果:

shape() before draw() Circle.draw(). radius = 0 shape() after draw() circle.circle(). radius = 5

new子类Circle对象时,会调用父类Shape的不带参数的构造方法,但是父类构造方法内调用了一个被子类Circle重写的方法draw(),根据结果可知,会调用子类的draw()方法;但是radius属性值却不是默认的1,而是0,这表明Circle对象还没有来得及构建,其radius属性为JVM给的默认值0,如果是引用类型的话则为null。所以在构造函数内,尽量避免使用实例方法,除了final和private方法。

毕!



【本文地址】

公司简介

联系我们

今日新闻


点击排行

实验室常用的仪器、试剂和
说到实验室常用到的东西,主要就分为仪器、试剂和耗
不用再找了,全球10大实验
01、赛默飞世尔科技(热电)Thermo Fisher Scientif
三代水柜的量产巅峰T-72坦
作者:寞寒最近,西边闹腾挺大,本来小寞以为忙完这
通风柜跟实验室通风系统有
说到通风柜跟实验室通风,不少人都纠结二者到底是不
集消毒杀菌、烘干收纳为一
厨房是家里细菌较多的地方,潮湿的环境、没有完全密
实验室设备之全钢实验台如
全钢实验台是实验室家具中较为重要的家具之一,很多

推荐新闻


图片新闻

实验室药品柜的特性有哪些
实验室药品柜是实验室家具的重要组成部分之一,主要
小学科学实验中有哪些教学
计算机 计算器 一般 打孔器 打气筒 仪器车 显微镜
实验室各种仪器原理动图讲
1.紫外分光光谱UV分析原理:吸收紫外光能量,引起分
高中化学常见仪器及实验装
1、可加热仪器:2、计量仪器:(1)仪器A的名称:量
微生物操作主要设备和器具
今天盘点一下微生物操作主要设备和器具,别嫌我啰嗦
浅谈通风柜使用基本常识
 众所周知,通风柜功能中最主要的就是排气功能。在

专题文章

    CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭