具体的过程是这样的:
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.
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.当执行 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”。
就如:
RoundGlyph(int r)
{
super();
radius = r;
System.out.println("RoundGlyph.RoundGlyph(), radius = " + radius);
}
有什么疑问??
----------------------------------------------------------
当你调用多态函数时 会根据你所使用的对象类型来决定调用哪一个 既然你
产生的是子类类型的对象 调用的应该就是子类的函数
----------------------------------------------------------
可是你应该注意到,子类类型的对象还并没有产生,还处于正在产生中的情
况下,当 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(淫贼) 一样,不能让我信服。
-------------------------------------------------------------
你写时RoundGlyph(int r),javac默认帮你加入了super();
-------------------------------------------------------------
这一点我知道,在我上面的执行步骤中,已说明了这一点。
如果和类的静态变量放在一起的话,那么在子类对象还没有构造的情况下是可以调用其方法的。
而且,我记得:为了提高效率,类的方法是只有一份的,所有对象都共用的。
既上面的假设应该是成立的。
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中就产生了。
当创建一个子类的实例时!
先1、执行父类的构造函数
再2、自己的构造函数!那么在执行父类的构造函数中,如果调用一个方法xxx()!又如果在子类中找到一个同样的方法
xxx(),我指参数个数、类型完全一样,那么优先执行子类中的,如果方法xxx()在子类中没有,那么就执行父类中的!
-----------------------------------------------------------------
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()没有返回值。
---------------------------------------
你记住继承是这样的!
当创建一个子类的实例时!
先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();
}
} ///:~
========================================
至于《thinking in java 》的那个例子应该更好懂。首先JVM加载类的时候先初始所以静态的,先基类后派生。然后执行main,当实例化一个类的时候先初始成员变量,再是构造函数。由于Beetle派生至Insect,所以先Insert后Beetle。
一个原则是无论是加载类还是实例化类,都必须先把基类的东西搞定之后才是派生类的。就像先有父后有子。还有静态的要先于一切。变量要先于函数。
--------------------------------------------------------------------
建议老兄去读读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的构造函数。
=======================================================================
RoundGlyph(int r)
{
super(); //←这样
radius = r;
System.out.println("RoundGlyph.RoundGlyph(),
radius = " + radius);
}