01 多态是什么

在我刻板的印象里,西游记里的那段孙悟空和二郎神的精彩对战就能很好的解释“多态”这个词:一个孙悟空,能七十二变;一个二郎神,也能七十二变;他们都可以变成不同的形态,但只需要悄悄地喊一声“变”。

Java的多态是什么呢?其实就是一种能力——同一个行为具有不同的表现形式;换句话说就是,执行一段代码,Java在运行时能根据对象的不同产生不同的结果。和孙悟空和二郎神都只需要喊一声“变”,然后就变了,并且每次变得还不一样;一个道理。

多态的前提条件有三个:

  • 子类继承父类
  • 子类覆盖父类的方法
  • 父类引用指向子类对象

多态的一个简单应用,来看程序清单1-1:

//子类继承父类
public class Wangxiaoer extends Wanger {
public void write() { // 子类覆盖父类方法
System.out.println("记住仇恨,表明我们要奋发图强的心智");
} public static void main(String[] args) {
// 父类引用指向子类对象
Wanger[] wangers = { new Wanger(), new Wangxiaoer() }; for (Wanger wanger : wangers) {
// 对象是王二的时候输出:勿忘国耻
// 对象是王小二的时候输出:记住仇恨,表明我们要奋发图强的心智
wanger.write();
}
}
} class Wanger {
public void write() {
System.out.println("勿忘国耻");
}
}

02 多态与后期绑定

现在,我们来思考一个问题:程序清单1-1在执行wanger.write()时,由于编译器只有一个Wanger引用,它怎么知道究竟该调用父类Wanger的write()方法,还是子类Wangxiaoer的write()方法呢?

答案是在运行时根据对象的类型进行后期绑定,编译器在编译阶段并不知道对象的类型,但是Java的方法调用机制能找到正确的方法体,然后执行出正确的结果。

多态机制提供的一个重要的好处程序具有良好的扩展性。来看程序清单2-1:

//子类继承父类
public class Wangxiaoer extends Wanger {
public void write() { // 子类覆盖父类方法
System.out.println("记住仇恨,表明我们要奋发图强的心智");
} public void eat() {
System.out.println("我不喜欢读书,我就喜欢吃");
} public static void main(String[] args) {
// 父类引用指向子类对象
Wanger[] wangers = { new Wanger(), new Wangxiaoer() }; for (Wanger wanger : wangers) {
// 对象是王二的时候输出:勿忘国耻
// 对象是王小二的时候输出:记住仇恨,表明我们要奋发图强的心智
wanger.write();
}
}
} class Wanger {
public void write() {
System.out.println("勿忘国耻");
} public void read() {
System.out.println("每周读一本好书");
}
}

在程序清单2-1中,我们在Wanger类中增加了read()方法,在Wangxiaoer类中增加了eat()方法,但这丝毫不会影响到write()方法的调用。write()方法忽略了周围代码发生的变化,依然正常运行。这让我想起了金庸《倚天屠龙记》里九阳真经的口诀:“他强由他强,清风拂山岗;他横由他横,明月照大江。”

多态的这个优秀的特性,让我们在修改代码的时候不必过于紧张,因为多态是一项让程序员“将改变的与未改变的分离开来”的重要特性。

03 多态与构造器

在构造器中调用多态方法,会产生一个奇妙的结果,我们来看程序清单3-1:

public class Wangxiaosan extends Wangsan {
private int age = 3;
public Wangxiaosan(int age) {
this.age = age;
System.out.println("王小三的年龄:" + this.age);
} public void write() { // 子类覆盖父类方法
System.out.println("我小三上幼儿园的年龄是:" + this.age);
} public static void main(String[] args) {
new Wangxiaosan(4);
// 上幼儿园之前
// 我小三上幼儿园的年龄是:0
// 上幼儿园之后
// 王小三的年龄:4
}
} class Wangsan {
Wangsan () {
System.out.println("上幼儿园之前");
write();
System.out.println("上幼儿园之后");
}
public void write() {
System.out.println("老子上幼儿园的年龄是3岁半");
}
}

从输出结果上看,是不是有点诧异?明明在创建Wangxiaosan对象的时候,年龄传递的是4,但输出结果既不是“老子上幼儿园的年龄是3岁半”,也不是“我小三上幼儿园的年龄是:4”。

为什么?

因为在创建子类对象时,会先去调用父类的构造器,而父类构造器中又调用了被子类覆盖的多态方法,由于父类并不清楚子类对象中的属性值是什么,于是把int类型的属性暂时初始化为0,然后再调用子类的构造器(子类构造器知道王小二的年龄是4)。

04 多态与向下转型

向下转型是指将父类引用强转为子类类型;这是不安全的,因为有的时候,父类引用指向的是父类对象,向下转型就会抛出ClassCastException,表示类型转换失败;但如果父类引用指向的是子类对象,那么向下转型就是成功的。

来看程序清单4-1:

public class Wangxiaosi extends Wangsi {
public void write() {
System.out.println("记住仇恨,表明我们要奋发图强的心智");
} public void eat() {
System.out.println("我不喜欢读书,我就喜欢吃");
} public static void main(String[] args) {
Wangsi[] wangsis = { new Wangsi(), new Wangxiaosi() }; // wangsis[1]能够向下转型
((Wangxiaosi) wangsis[1]).write();
// wangsis[0]不能向下转型
((Wangxiaosi)wangsis[0]).write();
}
} class Wangsi {
public void write() {
System.out.println("勿忘国耻");
} public void read() {
System.out.println("每周读一本好书");
}
}

05 总结

我喜欢把复杂的事情尽量简单化,把简单的事情有趣化——多态是Java的三大特性之一,它本来需要长篇大论的介绍,但我觉得实在没有必要,把关键的知识点提炼出来就足够了。更重要的是,你要通过实践去感知多态的优秀之处。

Java 技术驿站的chenssy对多态下了一个非常经典的结论,我们不妨大声的朗读几遍:

多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编译时并不确定,而是在程序运行期间才确定;即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。因为在程序运行时才确定具体的类,这样,不用修改源程序代码,就可以让引用变量绑定到各种不同的类实现上,从而导致该引用调用的具体方法随之改变,即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性。

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. nand flash和nor flash的区别

    NOR和NAND是现在市场上两种主要的非易失闪存技术. Intel于1988年首先开发出NOR flash技术,彻底改变了原先由EPROM和EEPROM一统天下的局面. 东芝于1989年开发出NAND ...

  2. 编写一份好的 Vimrc

    编写一份好的 Vimrc 目录 如何 Vimrc 色彩 空白字符与制表符 UI 配置 搜索 折叠 移动 用户自定义的前缀快捷按键 插件CtrlP 启动配置 终端Tmux 自动命令及其分组 备份 自定义 ...

  3. VS2017打包注册IE插件及修改IE安全选项设置

    前言 最近项目需要在浏览器环境下读取员工身份证信息,要实现网页与硬件设备通信,考虑了几种实现方式: 1.借助ActiveX插件,通过程序库直接与设备通信. 优点:厂家提供了IE插件,开发简单 缺点:只 ...

  4. Exp1 PC平台逆向破解 20165235 祁瑛

    Exp1 PC平台逆向破解 20165235 祁瑛 实践目标 本次实践的对象是一个名为pwn1的linux可执行文件.该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字 ...

  5. java新手入门

    参考地址 java博客 1.jdk    安装 /usr/libexec/java_home -V   查询jdk的版本 2.tomcat  安装教程 配置 mkdir -p /Library/Tom ...

  6. python no module named _socket 原因

    python no module named _socket 原因 Lib/site-packages 不在 sys.path 中

  7. 音频相关基本概念,音频处理及编解码基本框架和原理以及音、重采样、3A等音频处理(了解概念为主)

    视频笔记:音频专业级分析软件(Cooledit) 音质定义以语音带宽来区分,采样率越高,带宽越大,则保真度越高,音质越好.窄带(8khz采样),宽带(16khz采样),CD音质(44.1khz采样) ...

  8. leetcode刷题五<最长回文子串>

    下面是题目的描述 给定一个字符串 s,找到 s 中最长的回文子串.你可以假设 s 的最大长度为 . 示例 : 输入: "babad" 输出: "bab" 注意: ...

  9. net view ERROR 6118

    如上图,使用net view命令出现错误6118(server2012),谷歌百度油管解决了. 尝试1: 把Computer Browser服务打开.https://blog.csdn.net/qq_ ...

  10. [CF1132G]Greedy Subsequences

    [CF1132G]Greedy Subsequences 题目大意: 定义一个序列的最长贪心严格上升子序列为:任意选择第一个元素后,每次选择右侧第一个大于它的元素,直到不能选为止. 给定一个长度为\( ...