前言

在面向对象程序设计语言中,多态是继数据抽象和继承之后的第三种基本特性。多态的含义是什么,有什么作用以及在Java中是怎么实现的?下面将做介绍。

什么是多态

简单点说就是“一个接口,多种实现”,不同类对同一操作体现出不同效果。设想有一个性质,一个引用变量所指向的确切类型和该引用变量调用的方法是哪个类中的,这个两个问题在编译期间是不确定的,在程序运行期间才可确定。于是,一份代码就可以适用于多个不同的类,只要这份代码中有一个引用变量可以指向这些不同的类的对象。在程序运行期间,就可以动态选择多个不同的对象或者多个不同的方法运行,这就是多态性

也可以简单的使用Java核心卷中的一句话说:一个引用变量可以指示多种实际类型的现象被称为多态。

要什么样的引用变量才可以指向多种不同类的对象呢?那就需要是基类引用变量。这就涉及到向上转型

向上转型

简单一句话就是:基类(父类)引用指向子类对象。取这个术语属于也是有历史原因的,以传统的类继承图的绘制方法为基础:将根置于页面的顶端,然后逐渐向下。

为什么基类引用就可以指向子类对象呢?

这可以以生活中的一个例子来说,狗是对所有品种狗的统称,具体的品种又有哈士奇,拉布拉多犬等。假设我们在路上遇见了一只不知道名字的狗(比如牧羊犬),我们也许会说:那儿有一只狗。此时,我们就做了向上转型,以狗指向了具体的牧羊犬。以范围较大的基类引用去指向范围小的子类。这是以生活中的例子解释,在语言层面其实也可以说明:子类是继承基类而来,所以基类中的所有方法子类也有,可以发送给基类的消息同样也可以发送给子类。使用基类引用也就可以指向子类对象调用这些方法。

可以使用基类引用指向子类对象,那么可不可以使用子类引用指向基类对象呢?

是可以的,这就叫做向下转型,但是这存在风险。因为子类中可能会有新增方法,而基类中是没有这些方法的,若是调用这些方法就会抛出ClassCastException异常。

介绍了什么是多态,那么就了解它的作用有什么。

多态的作用

多态的一个好处就是可以实现统一管理,需要注意父类不能调用子类特有的方法即父类中没有的方法子类有的方法,若要调用需要向下转型。

多态如何实现

Java中实现多态的三个要求为:

  • 要有继承关系

  • 方法要被重写

  • 基类引用指向子类对象

可以看一个简单的多态例子

class Animal{
public void eat() { System.out.println("吃东西"); }
} class Dog extends Animal{
public void eat() { System.out.println("吃狗粮");}
} class Cat extends Animal{
public void eat() { System.out.println("吃小鱼干");}
} public class PloyTest {
//使用父类引用指向子类对象 调用子类中被重写的方法
public static void printEatingFood(Animal a) { a.eat();} public static void main(String[] args) {
printEatingFood(new Dog());
printEatingFood(new Cat());
}
}
/*
output:
吃狗粮
吃小鱼干
*/

当我们传入Dog对象时,a.eat()调用的是Dog类中被重写的eat方法;传入Cat对象时,a.eat()调用的是Cat类中被重写的方法。程序在运行过程中,依据我们传入的对象自动地为我们寻找到正确的方法调用。这就是多态技术的实现依据:动态绑定。

动态绑定

《Java编程思想》上这样说:运行时父类引用根据其指向的对象,绑定到相应的对象方法上。

那么这个过程的具体实现是怎样的呢?《Java核心技术卷1》上是这样解释的解释:

先是要清楚调用对象方法的过程:

  1. 搜索过程)编译器查看对象的声明类型和方法名。可能会有同名的重载方法。

若是调用x.f(param)

获得当前类和超类中为public的名为f的方法,即获可能被调用的候选方法

  1. 匹配过程)然后,编译器查看调用方法时提供的参数类型,与候选方法进行匹配,此过程也就是重载解析。

完全匹配,则选择调用该方法,这其中还会存在类型转换,所以过程会比较复杂。最后若是没有找到匹配的,编译器则会报错。

​ -------------------------------------------------------------------------------------------

静态绑定:
若方法是private, static, final或者构造器,那么编译器会知道调用哪个方法,这种方式为静态绑定。

动态绑定:
依赖于隐式参数的实际类型,在运行时才可以确定调用方法,则为动态绑定。
当程序运行,并且采用动态绑定调用方法时,虚拟机会调用与x所引用对象的实际类型最合适的那个类的方法。并且为了避免每次搜索浪费时间,虚拟机会为每个类创建一个方法表。其中包含所有方法的签名和实际调用的方法(包括继承来方法)。

一些陷阱和建议

域和静态方法

我们需要注意只有普通方法调用才可以是多态的,对域的访问将在编译时期进行解析。如下面这个例子:

class Super{
public int field = 0;
public int getField() { return field;}
} class Sub extends Super{
public int field = 1;
public int getField() { return field;}
public int getSuperField() { return super.getField();}
} public class FieldAccess {
public static void main(String[] args) {
Super sup = new Sub();
System.out.println("sup.field="+sup.field+" sup.getField()="+sup.getField()); Sub sub = new Sub();
System.out.println("sub.getField="+sub.field+" sub.getField()="+sub.getField()+
" sub.getSuperField()="+sub.getSuperField());
}
}
/*
output:
sup.field=0 sup.getField()=1
sub.getField=1 sub.getField()=1 sub.getSuperField()=0
*/

当使用父类Super的引用sup指向子类Sub类对象,输出域,发现是父类的值。因此,域的访问是编译器解析,不是多态的。

如果某个方法是静态的,它的行为也不具有多态性

class StaticSuper{
public static String staticGet() {
return "Base staticGet()";
} public String dynamicGet(){
return "Base dynamicGet()";
}
} class StaticSub extends StaticSuper{
public static String staticGet() {
return "Derived staticGet()";
} public String dynamicGet() {
return "Derived dynamicGet()";
}
} public class OverloadingTest {
public static void main(String[] args) {
StaticSuper sup = new StaticSub(); //向上转型
System.out.println(sup.staticGet());
System.out.println(sup.dynamicGet());
}
}
/*
output:
Base staticGet()
Derived dynamicGet()
*/

由输出可以看出,静态方法是不具有多态性的。静态方法是与类,而非与单个的对象相关联的。

小结

简要介绍了对于多态的理解,其中存在的不足,希望各位看官不吝赐教。

参考:

《Java编程思想》第四版

《Java核心技术卷1》第九版

Java多态性理解,好处及精典实例:https://blog.csdn.net/Jian_Yun_Rui/article/details/52937791

Java——多态浅析的更多相关文章

  1. Java 多态——与C++的比较

    学习了Java和C++之后,由于长期不使用C++,而java的基础知识掌握不牢,现在已经搞不清java多态了.现在先来谈谈java多态,稍后有时间再更新C++的多态,并进行比较~ 一. Java的多态 ...

  2. C++和java多态的区别

    C++和java多态的区别 分类: Java2015-06-04 21:38 2人阅读 评论(0) 收藏 举报  转载自:http://www.cnblogs.com/plmnko/archive ...

  3. 深入理解Java多态机制

    从字节码层面来看,Java中的所有方法调用,最终无外乎转换为如下几条调用指令. invokestatic: 调用静态方法. invokespecial: 调用实例构造器<init>方法,私 ...

  4. Java 多态 父类和子类方法的访问控制权限

    Java 多态 父类和子类方法的访问控制权限 @author ixenos 父类和子类方法的访问控制权限 继承是为了扩展类的功能,而这种扩展显然就是对一个原始类的扩展,目的还是向上转型来调用,所以这就 ...

  5. Java多态(二)

    public class ExtendsTest { public static void main(String[] args) { A a1 = new A(); A a2 = new B(); ...

  6. 从JVM角度看Java多态

    首先,明确一下,Java多态的三个必要条件: 1. 继承 2. 子类重写父类方法 3. 父类引用指向子类对象 然后看一个例子 package test.xing; class Father{ prot ...

  7. 关于java多态的理解

    要理解多态,就必须有一个大的理解方向,不然很容易绕进去. 首先知道多态的释义:多态性是指一个名词可以有多种语义. 对于java的多态性学习者来说,就是必须要知道多个同名方法在不同情况下的使用规则. j ...

  8. Java经验杂谈(2.对Java多态的理解)

    多态是面向对象的重要特性之一,我试着用最简单的方式解释Java多态: 要正确理解多态,我们需要明确如下概念:・定义类型和实际类型・重载和重写・编译和运行 其中实际类型为new关键字后面的类型. 重载发 ...

  9. 学JAVA第十六 天,JAVA多态

    今天老师讲了多态的使用 多态是同一个行为具有多个不同表现形式或形态的能力. 多态的优点: 1. 消除类型之间的耦合关系 2. 可替换性  3. 可扩充性 4. 接口性 5. 灵活性 6. 简化性 我个 ...

随机推荐

  1. Docker安装及基本操作

    系统环境 CentOS Linux release 7.5.1804 (Core) 安装依赖包 更新系统软件 yum update 安装docker yum install docker 启动dock ...

  2. timesten报错:error while loading shared libraries: libaio.so.1: cannot open shared object file : No such file or directory

    我遇到的这个错是因为缺少依赖:libaio 直接yum -y install libaio 然后重新安装就OK了

  3. centos7 ping不通 name or service not known

    最近打算为centos安装一个界面时,发现不能上网.ping www.baidu.com 报name or service not known. 原来网络配置没设好. 一.选择VMWare的NAT模式 ...

  4. django 源码报错

    启动django ,一直提示一个 AttributeError: 'str' object has no attribute 'decode' 哥,查了一下午google,就怕是自己判断错了,最后在一 ...

  5. [LeetCode] Bus Routes 公交线路

    We have a list of bus routes. Each routes[i] is a bus route that the i-th bus repeats forever. For e ...

  6. ES6-个人学习大纲

    1,let   const学习补充 1.1,let的知识点: 01-作用域只限制在当前代码块内,代码块形式如下: { var str = '张三'; console.log(str); let str ...

  7. django 标签的使用

    首先重建一个common的app 然后创建__init__使common成为一个包   注意templatetags 名字使固定的 并在下面创建一个名字为fitter的过滤器 注册过滤器app htm ...

  8. [python] bluepy 一款python封装的BLE利器

    1.bluepy 简介 bluepy 是github上一个很好的蓝牙开源项目,其地址在 LINK-1, 其主要功能是用python实现linux上BLE的接口. This is a project t ...

  9. 用R处理一组数据的三种方式

    USArrests是R附带的一个数据集,现在我们需要创建一个factor向量urbancat,如果UrbanPop列的某个值在中位数之上,就把urbancat对应位置的值设为1,否则设为0. 这种数据 ...

  10. JS实现数组去重方法大总结

    js数组根据对象中的元素去重: var arr2 = [ { name: "name1", num: "1" }, { name: "name2&qu ...