具体的过程是这样的:
RoundGlyph extends Glyph,在new一个RoundGlyph实例的时候先调用Glayph(),
这样就先执行了System.out.println("Glyph() before draw()");
在调用draw()方法的时候,在Glyph和RoundGlyph类中都有draw()方法,这就是多态。
多态是在运行时做判断的,由于你new的是一个RoundGlyph实例,所以RoundGlyph中的draw()方法将父类中的draw()方法覆盖掉,执行的就是RoundGlyph中的方法了。
接下来再运行System.out.println("Glyph() after draw()");
完成调用父类的构造函数后调用System.out.println("RoundGlyph.RoundGlyph(), radius = " + radius);程序结束。
第二个问题,RoundGlyph.radius尚未初始化,由于radius的类型是int,如果不初始化,默认的值就是0。如果是对象类型,输出的值就是null.

解决方案 »

  1.   

    我推测的运行步骤如下:

    1.当执行 new RoundGlyph(5) 时,java载入器根据classpath找到 RoundGlyph 
      class, 并载入 RoundGlyph class, 由于 RoundGlyph 继承自 Glyph ,因此
      载入器接着载入 Glyph.
    2.执行 Glyph class 的 static 变量的初始化动作,接着执行 RoundGlyph
      class 的 static 变量的初始化动作。(本例没有 static 变量)
    3.执行 Glyph class 中变量的初始化动作。(本例没有 Glyph class 的变量)
    4.执行 Glyph.Glyph()。输出"Glyph() before draw()",
    调用 Glyph.draw(),<--------此时不调用Glyph.draw(),而是调用RoundGlyph.draw()多态运行期进行邦定-------->
      输出"Glyph().draw()",接着输出"Glyph() after draw()"。
    5.执行 RoundGlyph 变量的初始化动作。int radius = 1;
    6.执行 RoundGlyph.RoundGlyph()。
    radius = 5;
    System.out.println("RoundGlyph.RoundGlyph(), radius = " + radius);
      输出“RoundGlyph.RoundGlyph(), radius = 5”。
      

  2.   

    你写时RoundGlyph(int r),javac默认帮你加入了super();
    就如:
    RoundGlyph(int r) 
    {
                 super();
        radius = r;
        System.out.println("RoundGlyph.RoundGlyph(), radius = " + radius);
       }
    有什么疑问??
      

  3.   

    to : alexlex(淫贼) 
    ----------------------------------------------------------
    当你调用多态函数时 会根据你所使用的对象类型来决定调用哪一个 既然你
    产生的是子类类型的对象 调用的应该就是子类的函数
    ----------------------------------------------------------
    可是你应该注意到,子类类型的对象还并没有产生,还处于正在产生中的情
    况下,当 new RoundGlyph(5);时,必须调用base class的构造方法
    Glyph()。但在Glyph()中又调用了draw().如果是这样
    RoundGlyph r = new RoundGlyph(5);
    r.draw();//这时调用的是子类方法
    你的说法是可以让我信服的。
    to : Mailbomb(网络咖啡) 
    -----------------------------------------------------------
    调用构造函数的时候会先调用父类的构造方法,这个是继承
    -----------------------------------------------------------
    我知道是继承,你并没有回答我的问题。
    to: wanglh2000() 
    -----------------------------------------------------------
    具体的过程是这样的:
    RoundGlyph extends Glyph,在new一个RoundGlyph实例的时候先调用
    Glayph(),这样就先执行了System.out.println("Glyph() before 
    draw()");在调用draw()方法的时候,在Glyph和RoundGlyph类中都有
    draw()方法,这就是多态。
    多态是在运行时做判断的,由于你new的是一个RoundGlyph实例,所以
    RoundGlyph中的draw()方法将父类中的draw()方法覆盖掉,执行的就是
    RoundGlyph中的方法了。
    接下来再运行System.out.println("Glyph() after draw()");
    完成调用父类的构造函数后调用System.out.println("RoundGlyph.
    RoundGlyph(), radius = " + radius);程序结束。
    -----------------------------------
    同to:alexlex(淫贼) 一样,不能让我信服。-----------------------------------
    第二个问题,RoundGlyph.radius尚未初始化,由于radius的类型是int,
    如果不初始化,默认的值就是0。如果是对象类型,输出的值就是null.
    ------------------------------------------------------------
    这个上面我也说到了,我想知道radius是在那一步获得的。就象上面我所列
    出的步骤,可以列出详细的步骤吗?
    to: zhuam(阿明)
    ------------------------------------------------------------
    父类的构造函数不能够被继承的
    千万记住!!!
    ------------------------------------------------------------
    只有class能被继承,没有构造函数被继承这一说法。
    to: flash007() 
    ------------------------------------------------------------
    此时不调用Glyph.draw(),而是调用RoundGlyph.draw()多态运行期进行邦定
    -------------------------------------------------------------
    同to:alexlex(淫贼) 一样,不能让我信服。
      

  4.   

    to: Hodex(小何才露尖尖角)
    -------------------------------------------------------------
    你写时RoundGlyph(int r),javac默认帮你加入了super();
    -------------------------------------------------------------
    这一点我知道,在我上面的执行步骤中,已说明了这一点。
      

  5.   

    关于到底调用的是谁的draw()方法,我有个问题:类的方法是在地方存储的?
    如果和类的静态变量放在一起的话,那么在子类对象还没有构造的情况下是可以调用其方法的。
    而且,我记得:为了提高效率,类的方法是只有一份的,所有对象都共用的。
    既上面的假设应该是成立的。
      

  6.   

    我觉得继承应该是很简单的问题,比起后面的多形和动态绑定要好理解的多啊,如果你这个都没有搞明白后面的很困难。
    1、关于constructor的初始化,它是在method之前的,无论你是否把method写在constructor前后,编译器都先是初始化constructor。(最终调用还是看调用的语句顺序,你可以把这个程序中的method和constructor调换一下顺序再看看结果。)
    2、在继承中derived class中的method会覆盖base class的同方法(是method的名称和参数都一样,只是方法体不同),base-class's constructor don't extend。(我觉得这些你应该知道的啊)
    在derived class中有方法:
    void draw() 

        System.out.println("RoundGlyph.draw(), radius = " + radius);
       }
    它是覆盖了base class中的同方法,所以在最终调用draw()时,实际上调用的是derivedclass.draw()而不是baseclass.draw()。所以本来base class中的constructor是调用本类里的draw,但由于derivedclass.draw()覆盖了baseclass.draw(),最终结果的第二行是RoundGlyph.draw(),radius=0(0的产生就不说了);如果你把上面的程序修改为:
    void draw() 

        System.out.println("RoundGlyph.draw(), radius = " + radius+"\n"+
                 super.draw());
       }
    重要的是看super.draw()会产生什么结果。
    3、radius的值是在int r中就产生了。
      

  7.   

    你记住继承是这样的!
    当创建一个子类的实例时!
    先1、执行父类的构造函数
    再2、自己的构造函数!那么在执行父类的构造函数中,如果调用一个方法xxx()!又如果在子类中找到一个同样的方法
    xxx(),我指参数个数、类型完全一样,那么优先执行子类中的,如果方法xxx()在子类中没有,那么就执行父类中的!
      

  8.   

    to: addwart(灌水专用) 
    -----------------------------------------------------------------
    1、关于constructor的初始化,它是在method之前的,无论你是否把method写在
    constructor前后,编译器都先是初始化constructor。(最终调用还是看调用的
    语句顺序,你可以把这个程序中的method和constructor调换一下顺序再看看结果。)
    -----------------------------------------------------------------
    constructor的初始化是什么意思,是指constructor调用吗?constructor是用来
    初始化class object的,不存在constructor的说法。当object产生时,就调用
    constructor,当然和constructor的位置无关了。另外也不存在method初始化的
    说法。method根本无需初始化呀。只有class的数据成员要初始化。------------------------------------------------------------------
    2、在继承中derived class中的method会覆盖base class的同方法(是method的
    名称和参数都一样,只是方法体不同),base-class's constructor don't 
    extend。(我觉得这些你应该知道的啊)void draw() 

    System.out.println("RoundGlyph.draw(), radius = " + radius+ "\n"
    + super.draw());
    }重要的是看super.draw()会产生什么结果。
    ------------------------------------------------------------------
    从程序运行的结果可以看出 Glyph() 确实调用了RoundGlyph.draw().这是很明显的
    事,我也知道形成这种结果一定是多态(动态绑定)机制在起作用。
    我只是不理解,动态绑定在子类对象没生成之前(正在初始化时)就起作用了。
    你上面所改的程序的输出和没改前一样。因为super.draw()没有返回值。
      

  9.   

    to:  zhjjava(狂人一个) 
    ---------------------------------------
    你记住继承是这样的!
    当创建一个子类的实例时!
    先1、执行父类的构造函数
    再2、自己的构造函数!那么在执行父类的构造函数中,如果调用一个方法xxx()!又如果在子类中找到一个同样
    的方法xxx(),我指参数个数、类型完全一样,那么优先执行子类中的,如果方法xxx()
    在子类中没有,那么就执行父类中的!
    ---------------------------------------程序运行的结果正象你所说的那样。
    或许我该换一种提问方式。
    就是当产生class 的一个object时,javac和java都在幕后进行了哪些工作?
    这个程序的用意正在于此,在构造方法及方法中输出一些信息就是为了揭示
    object的初始化过程。这个例子来自侯杰译的《thinking in java 》第239页的例子。
    不过我看了几遍,没看明白。不过这个例子还不具有普遍代表性,因为当有静态数据成员时,情况还要复杂。
    还有一个例子我贴出来,大家先别运行,自已推测一下运行的结果,看看与实际
    运行结果是否一致。========================================
    //: c06:Beetle.java
    // From 'Thinking in Java, 2nd ed.' by Bruce Eckel
    // www.BruceEckel.com. See copyright notice in CopyRight.txt.
    // The full process of initialization.class Insect 
    {
    int i = 9;
    int j;
    Insect() 
    {
    prt("i = " + i + ", j = " + j);
    j = 39;
    }
    static int x1 = prt("static Insect.x1 initialized");
    static int prt(String s) 
    {
    System.out.println(s);
    return 47;
    }
    }public class Beetle extends Insect 
    {
    int k = prt("Beetle.k initialized");
    Beetle() 
    {
    prt("k = " + k);
    prt("j = " + j);
    }
    static int x2 = prt("static Beetle.x2 initialized");
    public static void main(String[] args) 
    {
    prt("Beetle constructor");
    Beetle b = new Beetle();
    }
    } ///:~
    ========================================
      

  10.   

    建议老兄去读读JVM的技术规范,我已经很习惯看到上面这种代码啦。打个比方new RoundGlyph(5)的时候会给每个函数传一个指针,这个指针就是this,当调用RoundGlyph的构造函数时先调用super(),这个时候也把这个this传进去,那么Glyph里面的this就指的是RoundGlyph如果再调用draw()(实际上是this.draw())你认为是调用哪个draw呢?显然是RoundGlyph的。建议大家以后看这些函数的时候都把this添上,特别是调用函数的时候,也要把this传进去。
    至于《thinking in java 》的那个例子应该更好懂。首先JVM加载类的时候先初始所以静态的,先基类后派生。然后执行main,当实例化一个类的时候先初始成员变量,再是构造函数。由于Beetle派生至Insect,所以先Insert后Beetle。
    一个原则是无论是加载类还是实例化类,都必须先把基类的东西搞定之后才是派生类的。就像先有父后有子。还有静态的要先于一切。变量要先于函数。
      

  11.   

    glyph()调用的不是roundglyph中的draw(),而是本类base class中的draw(),但由于base.draw()被derive.draw()覆盖了,所以最终结果是产生derive.draw()
      

  12.   

    to:  liujiboy(liujiboy) 
    --------------------------------------------------------------------
    建议老兄去读读JVM的技术规范,我已经很习惯看到上面这种代码啦。打个比方
    new RoundGlyph(5)的时候会给每个函数传一个指针,这个指针就是this,当调用
    RoundGlyph的构造函数时先调用super(),这个时候也把这个this传进去,那么Glyph
    里面的this就指的是RoundGlyph如果再调用draw()(实际上是this.draw())你认为
    是调用哪个draw呢?显然是RoundGlyph的。建议大家以后看这些函数的时候都把this添上,特别是调用函数的时候,
    也要把this传进去。至于《thinking in java 》的那个例子应该更好懂。首先JVM加载类的时候先初始静
    态的,先基类后派生。然后执行main,当实例化一个类的时候先初始成员变量,再是构造函数。
    由于Beetle派生至Insect,所以先Insert后Beetle。一个原则是无论是加载类还是实例化类,都必须先把基类的东西搞定之后才是派生类的。就像
    先有父后有子。还有静态的要先于一切。变量要先于函数。
    ----------------------------------------------------------------------说的有道理。当初我的想法是,当在derived class 中调用super()时,好象产生了一
    个base class的object.所以在base class中的构造函数中再调用另一个函数(这个函
    数被derived class覆写)当然调用base class的函数。
    其实在derived class 中调用super()时,并不产生base class的object.只是为产
    生derived class 的object做准备工作。下面我想,把object产生过程中每一步列出来,可能不完善,希望大家帮我把它完善起
    来。弄清初始化过程中的每个细节。======================================================================
    1.  当产生derived class的一个object时,JVM根据classpath的设定,查找到该
        derived class,并载入它。2.  接着JVM找到base class,并载入它。3.  执行base class的static 变量定义处的初始化动作。执行顺序根据static变量
        在base class中定义的顺序。若static变量在定义处并未赋值,则把该static
        变量设为默认值。(即:基本类型变量设为0或false,object变量设为null)4.  执行derived class中的static变量定义外的初始化动作。同上。5.  JVM为derived class object在heap中分配存储空间。6.  这个存储空间被清零。对象中的所有基本类型变量和对象变量被设为默认值(所有
        变量包括derived class继承自base class的变量)7.  执行base class中成员变量(非static变量)定义处的初始化动作(按定义顺序)。8.  调用base class的构造函数(若在此构造函数中调用了一个被derived class覆
        写的函数,则调用derived class中的函数)。9.  执行derived class中成员变量(非static变量)定义处的初始化动作。10. 调用derived class的构造函数。  
    =======================================================================
      

  13.   

    值要该一个地方,思路就能理的很清楚了:
    RoundGlyph(int r) 
    {
                 super();  //←这样
        radius = r;
        System.out.println("RoundGlyph.RoundGlyph(), 
                                                  radius = " + radius);
       }