`

java 之多态(转)

    博客分类:
  • java
阅读更多

 转自:http://skyuck.iteye.com/blog/870217

 

关于java多态之前也写过一篇文章 
java多态之父子构造器 

今天看了java编程思想多态那章节,觉得还是有几个地方需要做一个记录 
1.多态与域 
    当我了解多态机制的时候,我认为所有的事物都可以多态的发生,然而事实上之后普通的方法调用才是多态的,例如,如果你直接访问某个域,这个访问就将在编译器就进行解析,而不是通过动态绑定进行的。 

Java代码  收藏代码
  1. package com.unis.ploy.music;  
  2.   
  3. public class FieldAccess {  
  4.   
  5.     /** 
  6.      * @param args 
  7.      */  
  8.     public static void main(String[] args) {  
  9.           
  10.         Super sub = new Sub();  
  11.         System.out.println("sub.field="+sub.field+"  ,sub.getField()="+sub.getField());  
  12.           
  13.         Sub sub1 = new Sub();  
  14.         System.out.println("sub1.field="+sub1.field+"  ,sub1.getField()="+sub1.getField()+"  ,sub1.getSuperField()="+sub1.getSuperField());  
  15.     }  
  16. }  
  17.   
  18. class Super{  
  19.     public int field=0;  
  20.       
  21.     public int getField(){  
  22.         return field;  
  23.     }  
  24. }  
  25.   
  26. class Sub extends Super{  
  27.       
  28.     public int field = 1;  
  29.       
  30.     public int getField(){  
  31.         return field;  
  32.     }  
  33.       
  34.     public int getSuperField(){  
  35.         return super.field;  
  36.     }  
  37. }  



我们可以看到上面的代码的运行结果如下: 

引用

sub.field=0  ,sub.getField()=1 
sub1.field=1  ,sub1.getField()=1  ,sub1.getSuperField()=0 



当Sub对象转型为Super引用时,任何域访问操作都将由编译器解析,因此域的操作不是多态的,在本例中,为Super.field和Sub.field分配了不同的存储空间。这样,Sub实际上包含两个称为field的域:它自己的和从Super处得到的。然而,在引用Sub中的field时所产生的默认域并非Super的field域,因此,为了得到Super.field,必须显式的指明super.field. 

2.构造器和多态 
编程思想:构造器并不具有多态性(它们实际上是static方法,只不过该static申明是隐式的) 

在上篇文章中我们知道基类的构造器总是在导出类的构造过程中被调用。其实这样做是有意义的,因为构造器具有一项特殊的任务,检查对象是否被正确构造。导出类只能访问它自己的成员,不能访问基类中的成员,只有基类的构造器才能对自己的元素进行初始化,因此在一个继承的类层次结构中构造器的调用顺序为: 

  • 调用基类的构造器,这个步骤会不断的反复递归下去,首先是构造层次的根,然后是下一层导出类,等等,直到最底层的导出类
  • 按声明顺序调用成员的初始化方法
  • 调用导出类构造器的主体


3.构造器内部的多态方法的行为 
我们知道构造器调用的层次机构,先调用基类的构造器,再调用导出类的构造器,这样就会带来一个有趣的两难问题。 
如果在一个基类的构造器的内部调用正在构造的对象(这个对象还没有被构造出来)的某个动态绑定方法,将会出现什么情况呢。 

例如下面的例子 

Java代码  收藏代码
  1. package com.unis.ploy.music;  
  2.   
  3. class Glyph{  
  4.     void draw(){  
  5.         System.out.println("Glyph.draw()");  
  6.     }  
  7.     public Glyph() {  
  8.         System.out.println("Glyph() before draw()");  
  9.         draw();  
  10.         System.out.println("Glyph() after draw()");  
  11.     }  
  12. }  
  13.   
  14. class RtGlyph extends Glyph{  
  15.       
  16.     private int radius = 1;  
  17.       
  18.     public RtGlyph(int r) {  
  19.         radius = r;  
  20.         System.out.println("RgGlyph.RtGlyph(),radius="+radius);  
  21.     }  
  22.       
  23.     void draw(){  
  24.         System.out.println("RgGlyph.draw(),radius="+radius);  
  25.     }  
  26. }  
  27.   
  28.   
  29. public class RoundGlyph extends Glyph{  
  30.       
  31.     public static void main(String[] args) {  
  32.         new RtGlyph(5);  
  33.     }  
  34.   
  35. }  


让我们看一下控制台输出信息 

引用

Glyph() before draw() 
RgGlyph.draw(),radius=0 
Glyph() after draw() 
RgGlyph.RtGlyph(),radius=5 

第一次radius的输出信息为0,而不是1。 
让我们来分析出现这种情况的原因吧,当执行new RtGlyph(5)时,我们知道先会执行基类的构造器,也就是说会先执行Glyph()构造方法,在Glyph()构造方法里面又调用了一个动态绑定的方法,我们都知道对于一个动态绑定的方法,会先从导出类中去找,如果导出类覆盖了基类的方法,就会调用导出类中的方法,这个时候也就是会调用导出类RtGlyph的draw()方法,可以这个时候导出类RtGlyph还没有被构造,因此类中的成员radius还没有被分配内存,因此第一次就输出radius=0。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics