多态
2016-06-19 13:40
218 查看
在了解多态之前,先了解向上类型转换和向下类型转换。
向上类型转换:由导出类向基类的转换,比如说基类是Animal,导出类是Dog,Cat等,则由Dog转换成Animal称为向上类型转换。向上类型转换是安全的,因为导出类是基类的超集,至少含有基类的所有方法,但可能丢失方法。向上类型转换不需要显示指定。
向下类型转换:由基类向导出类的转换,比如说从Animal转换为Dog,向下类型转换是不安全的,转换时需要显示指定。
多态的技术——后期绑定
所谓绑定就是将一个方法调用同一个方法体关联起来,而后期绑定就是在运行时根据对象的类型进行绑定,又叫做动态绑定和运行时绑定。与后期绑定对应的是前期绑定,即程序执行前进行绑定。
Java中除了static方法和final方法之外,其他所有的方法都是后期绑定。
多态是面向对象的三大特性(封装、继承、多态)之一,封装通过合并特征和行为来创建新的数据类型,多态通过向上类型转换,运行时决定调用哪个方法体,产生正确的行为。多态使得从相同基类导出的类可以当做同一类型对待,可以对这些不同类型的导出类调用基类中共有的方法。多态有下列特点:
1、多态分离了做什么和怎么做,做什么是指调用的某个方法,怎么做是指具体调用的是哪个方法体,因此多态消除了类型之间的耦合关系。
2、多态可以改善代码的组织结构和可读性,创建可扩展的程序。
3、多态是依靠后期绑定实现的,因此不是后期绑定的方法都不具有多态性,如final方法、static方法,private方法默认是final的,因此也不具有多态性。
4、成员变量不具有多态性;当你直接访问某个成员变量时,这个访问将在编译期进行解析。当导出类定义的变量与基类变量有相同的变量名field时,实际上导出类包含两个称为field的域,一个是从基类继承而来的,一个是导出类自己定义的,它们占用了不同的存储空间。
使用多态时的注意事项:
尽量不要在构造方法内部调用正在构造的对象的某个动态绑定方法,这么做会出现一些问题。如果我们在构造方法内部调用某个多态方法,则有可能这个方法所操纵的成员还未进行初始化。以下面的代码为例。
运行结果如下:
第一次创建子类对象
父类静态变量初始化
父类静态代码块初始化
子类静态变量初始化
子类静态块初始化
父类非静态变量初始化
父类构造方法开始执行
Glyph() before draw()
RoundGlyph.draw(), radius = 0
Glyph() after draw()
父类构造方法完成
子类非静态变量初始化
子类构造方法开始执行
RoundGlyph.RoundGlyph(), radius = 2
子类构造方法完成
第二次创建子类对象
父类非静态变量初始化
父类构造方法开始执行
Glyph() before draw()
RoundGlyph.draw(), radius = 0
Glyph() after draw()
父类构造方法完成
子类非静态变量初始化
子类构造方法开始执行
RoundGlyph.RoundGlyph(), radius = 3
子类构造方法完成
由以上运行结果可以看出当首次采用new创建一个子类对象时,继承结构中初始化的顺序为:
1、加载父类,完成基类中的静态初始化
2、加载子类,完成导出类中的静态初始化
3、父类非静态初始化
4、调用父类的构造方法
5、子类非静态初始化
6、调用子类构造方法
当第二次采用new生成RoundGlyph的对象时,就不进行静态初始化了,因为静态初始化只在类被加载的时候执行一次。
在本例中,父类的构造方法中调用了多态方法draw(),当调用父类的构造方法时,执行到draw()处,根据多态,此时会调用子类RoundGlyph中的draw()方法体,而此时RoundGlyph中的成员变量radius还未进行初始化,根据《Java编程思想》中的“在其他任何事物发生之前,将分配给对象的存储空间初始化成二进制的零,这时的radius的值为0,打印出RoundGlyph.draw(),
radius = 0。试想如果未经初始化的是某个对象的引用,则此时就会抛出空指针异常。因此,尽量不要在构造方法内部调用正在构造的对象的某个动态绑定方法。
向上类型转换:由导出类向基类的转换,比如说基类是Animal,导出类是Dog,Cat等,则由Dog转换成Animal称为向上类型转换。向上类型转换是安全的,因为导出类是基类的超集,至少含有基类的所有方法,但可能丢失方法。向上类型转换不需要显示指定。
向下类型转换:由基类向导出类的转换,比如说从Animal转换为Dog,向下类型转换是不安全的,转换时需要显示指定。
多态的技术——后期绑定
所谓绑定就是将一个方法调用同一个方法体关联起来,而后期绑定就是在运行时根据对象的类型进行绑定,又叫做动态绑定和运行时绑定。与后期绑定对应的是前期绑定,即程序执行前进行绑定。
Java中除了static方法和final方法之外,其他所有的方法都是后期绑定。
多态是面向对象的三大特性(封装、继承、多态)之一,封装通过合并特征和行为来创建新的数据类型,多态通过向上类型转换,运行时决定调用哪个方法体,产生正确的行为。多态使得从相同基类导出的类可以当做同一类型对待,可以对这些不同类型的导出类调用基类中共有的方法。多态有下列特点:
1、多态分离了做什么和怎么做,做什么是指调用的某个方法,怎么做是指具体调用的是哪个方法体,因此多态消除了类型之间的耦合关系。
2、多态可以改善代码的组织结构和可读性,创建可扩展的程序。
3、多态是依靠后期绑定实现的,因此不是后期绑定的方法都不具有多态性,如final方法、static方法,private方法默认是final的,因此也不具有多态性。
4、成员变量不具有多态性;当你直接访问某个成员变量时,这个访问将在编译期进行解析。当导出类定义的变量与基类变量有相同的变量名field时,实际上导出类包含两个称为field的域,一个是从基类继承而来的,一个是导出类自己定义的,它们占用了不同的存储空间。
使用多态时的注意事项:
尽量不要在构造方法内部调用正在构造的对象的某个动态绑定方法,这么做会出现一些问题。如果我们在构造方法内部调用某个多态方法,则有可能这个方法所操纵的成员还未进行初始化。以下面的代码为例。
package com.thinkingInJava; public class PolyTest { public static void main(String[] args) { System.out.println("第一次创建子类对象"); new RoundGlyph(2); System.out.println("第二次创建子类对象"); new RoundGlyph(3); } } class Glyph { private static String sup = "父类静态变量初始化"; static { System.out.println(sup); System.out.println("父类静态代码块初始化"); } private String gfield = "父类非静态变量初始化"; { System.out.println(gfield); } void draw() { System.out.println("Glyph.draw()"); } Glyph() { System.out.println("父类构造方法开始执行"); System.out.println("Glyph() before draw()"); draw(); System.out.println("Glyph() after draw()"); System.out.println("父类构造方法完成"); } } class RoundGlyph extends Glyph { private int radius = 1; private static String sub = "子类静态变量初始化"; static { System.out.println(sub); System.out.println("子类静态块初始化"); } private String rfield = "子类非静态变量初始化"; { System.out.println(rfield); } RoundGlyph(int r) { System.out.println("子类构造方法开始执行"); radius = r; System.out.println("RoundGlyph.RoundGlyph(), radius = " + radius); System.out.println("子类构造方法完成"); } void draw() { System.out.println("RoundGlyph.draw(), radius = " + radius); } }
运行结果如下:
第一次创建子类对象
父类静态变量初始化
父类静态代码块初始化
子类静态变量初始化
子类静态块初始化
父类非静态变量初始化
父类构造方法开始执行
Glyph() before draw()
RoundGlyph.draw(), radius = 0
Glyph() after draw()
父类构造方法完成
子类非静态变量初始化
子类构造方法开始执行
RoundGlyph.RoundGlyph(), radius = 2
子类构造方法完成
第二次创建子类对象
父类非静态变量初始化
父类构造方法开始执行
Glyph() before draw()
RoundGlyph.draw(), radius = 0
Glyph() after draw()
父类构造方法完成
子类非静态变量初始化
子类构造方法开始执行
RoundGlyph.RoundGlyph(), radius = 3
子类构造方法完成
由以上运行结果可以看出当首次采用new创建一个子类对象时,继承结构中初始化的顺序为:
1、加载父类,完成基类中的静态初始化
2、加载子类,完成导出类中的静态初始化
3、父类非静态初始化
4、调用父类的构造方法
5、子类非静态初始化
6、调用子类构造方法
当第二次采用new生成RoundGlyph的对象时,就不进行静态初始化了,因为静态初始化只在类被加载的时候执行一次。
在本例中,父类的构造方法中调用了多态方法draw(),当调用父类的构造方法时,执行到draw()处,根据多态,此时会调用子类RoundGlyph中的draw()方法体,而此时RoundGlyph中的成员变量radius还未进行初始化,根据《Java编程思想》中的“在其他任何事物发生之前,将分配给对象的存储空间初始化成二进制的零,这时的radius的值为0,打印出RoundGlyph.draw(),
radius = 0。试想如果未经初始化的是某个对象的引用,则此时就会抛出空指针异常。因此,尽量不要在构造方法内部调用正在构造的对象的某个动态绑定方法。
相关文章推荐
- 软件架构要达成的目的究竟是什么?
- Spring 任务
- HRESULT
- 第16周学习进度情况
- 数据库复习⑨
- C语言中生产随机数 rand()函数
- 软件开发文档编制的质量要求
- Blender 工具使用——模式切换
- ios 事件穿透
- 信息计量学复习重点(武大赵蓉英老师)
- Blender 工具使用——模式切换
- 1017 - Brush (III)
- 如何使用树莓派CPU挖矿(没有测试成功)
- Multinomial Logistic Regression
- css3基础、(弹性、响应式)布局注意点
- 80老翁谈人生(6):一生追梦的历程
- 系统中的插件和控件
- 美丽的图案
- 中间件技术的思想、概念和分类
- solve SetDefaultDllDirectories’ : is not a member of ‘`global namespace” from line 638 in atlcore.h