您的位置:首页 > 其它

多态

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的域,一个是从基类继承而来的,一个是导出类自己定义的,它们占用了不同的存储空间。

使用多态时的注意事项:

尽量不要在构造方法内部调用正在构造的对象的某个动态绑定方法,这么做会出现一些问题。如果我们在构造方法内部调用某个多态方法,则有可能这个方法所操纵的成员还未进行初始化。以下面的代码为例。

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。试想如果未经初始化的是某个对象的引用,则此时就会抛出空指针异常。因此,尽量不要在构造方法内部调用正在构造的对象的某个动态绑定方法。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: