下面是一个关于多态的示例,下面将主要针对这个例子进行分析:
package javaDemo.unit8; import java.io.ObjectInputStream.GetField; class Glyph{ int field1 = 1; static int field2 =2; public String getField(){ return "Glyph: field1 = " + field1 + ", field2 = " + field2; } void draw(){ System.out.println("Glyph.draw()"); } static void info(){ System.out.println("Glyph.static info()"); } Glyph(){ System.out.println("Glyph() before info()"); info(); System.out.println("Glyph() after info()"); System.out.println("Glyph() before draw()"); draw(); System.out.println("Glyph() after draw()"); } } class RoundGlyph extends Glyph{ int field1 = 3; static int field2 = 4; private int radius = 1; public String getField(){ return "RoundGlyph: field1 = " + field1 + ", filed2 = " + field2; } public String getSuperField(){ return "SuperFiled: field1 = " + super.field1 + ", filed2 = " + field2; } public RoundGlyph(int r) { radius = r; System.out.println("RoundGlyph.RoundGlyph(), radius = " + radius); } void draw(){ System.out.println("RoundGlyph.draw(), radius = " + radius); } static void info(){ System.out.println("RoundGlyph.info()"); } } public class PolyConstructors { public static void main(String[] args) { Glyph g = new RoundGlyph(7); g.draw(); g.info(); System.out.println("g.field1 = " + g.field1 + ", g.field2 = " + g.field2); System.out.println(g.getField()); } }
输出如下:
Glyph() before info()
Glyph.static info()
Glyph() after info()
Glyph() before draw()
RoundGlyph.draw(), radius = 0
Glyph() after draw()
RoundGlyph.RoundGlyph(), radius = 7
RoundGlyph.draw(), radius = 7
Glyph.static info()
g.field1 = 1, g.field2 = 2
RoundGlyph: field1 = 3, filed2 = 4
通过以前对多态学习,很容易分析出最下的输出:
①RoundGlyph: field1 = 3, filed2 = 4
这是由于Java中除了static方法和final方法(private方法也属于final方法)之外,其他所有的方法都是动态绑定的。
绑定:将一个方法调用同一个方法主体关联起来。
绑定分为:前期绑定和动态绑定(后期绑定,运行时绑定)
前期绑定:在程序执行前进行绑定
后期绑定:运行时根据对象的类型进行绑定。
而对于输出
②g.field1 = 1, g.field2 = 2
这是由于对于filed的访问操作,是由编译器进行解析的,不是多态的。所以输出直接为父类的field的值。
在实际的开发过程中,我们通常将filed设置为private,然后通过get方法获取到值,所以获取的为子类的field的值。这又一次提醒我们,不要为子类和基类中的field设置相同的名字。
③可以看出在进行初始化时,基类的构造器会首先被调用。
这是由于导出类一般只能访问自己的成员,不能访问基类的成员,只有基类的构造器能够适当的完成对自己元素的初始化。而且子类可以访问基类中的为public和protected成员,来完成子类的初始化,所以必须让所有的构造器都得到调用,才能构造出完整的对象,才能保证子类对象从所有直接或间接父类中继承的实例变量都被正确的初始化。
④
Glyph() before info()
Glyph.static info()
Glyph() after info()
info(),为static方法前期绑定,所以调用的为基类的方法。
⑤
Glyph() before draw()
RoundGlyph.draw(), radius = 0
Glyph() after draw()
RoundGlyph.RoundGlyph(), radius = 7
可以看出在子类初始化时,执行父类构造方法时,构造方法内的方法仍然为动态绑定。
我们来分析一下初始化的顺序:
1)最初,将分配给对象的存储空间初始化为二进制的零。
2)如果初始化的类存在父类,则调用父类的构造方法,构造方法内的方法调用遵循动态调用的规则(此时初始化类的构造方法还未被调用,所以radius的值为0)。
3)按照声明的顺序调用成员的初始化方法。
4)调用初始化类的构造方法。
这也提醒我们:我们应当使用尽可能简单的方法使对象进入正常的状态;尽量避免调用其他方法。在构造方法内能够安全调用的方法是基类的final方法(或private方法)。