关于父类构造函数调用子类成员的困惑

syscoder 2006-04-27 04:04:50
tij中的习题:
class BaseWithPrint {
public BaseWithPrint() {
p.println("BaseWithPrint()");
print();
}
public void print() {
System.out.println("BaseWithPrint.print");
}
}

class DerivedWithPrint extends BaseWithPrint {
int i = 47;
public DerivedWithPrint() {
p.println("DerivedWithPrint()");
}
public void print() {
System.out.println("i = " + i);
}
}

public class GetFile {

public static void main( String args[] ){
DerivedWithPrint dp = new DerivedWithPrint();
dp.print();
}
}
输出结果如下:
在父类构造函数BaseWithPrint()中调用print(),既然是在父类的构造函数里,那么子类自然还没有构造,其成员函数按说也应该是不存在的呀!可是为什么这时可以调用子类的print()呢?
小弟初学java,问得可能比较幼稚,哪位前辈指点一下吧!
...全文
509 23 打赏 收藏 转发到动态 举报
写回复
用AI写文章
23 条回复
切换为时间正序
请发表友善的回复…
发表回复
UnAgain 2006-04-28
  • 打赏
  • 举报
回复
一、运行时刻
a +----+ -+---+-
| |
| | A
| |
b +----+ -+- C
| |
| | B
| |
c +----+ -+---+-

我先用这个图表示类及其子类在内存中的逻辑概念上的分布。
其中:
1) 图形中的方框表示类在内存中的存储范围。
2) A表示从a点到b点的内存区
3) B表示从b点到c点的内存区
4) C表示从a点到c点的内存区

现在有一个类SuperClass及其子类SubClass被加载到内存。其中SuperClass加载于内存区A,那么SubClass占用哪一部分内存呢?

正确答案是C而不是B。

二,从代码角度观察
对于继承,我们首先应该这样理解,代码中使用继承,只是为了减少代码量(那位,先把砖头放下),而不管父类还是子类,都是相对独立的类。

当一个子类继承自其父类时,从代码角度,就是把其父类的代码重写一遍,然后再加上额外的代码,从而,成其子类。其实,这一工作是编译器完成的,当然也不是重写代码,但是我们可以这么直观的理解。

我按照这种思路改造你的DerivedWithPrint,得到下面的代码:

// 注意,去掉了extends
class DerivedWithPrint {
int i = 47;
public DerivedWithPrint() {

//来自BaseWithPrint
p.println("BaseWithPrint()");
print();

// 本地
p.println("DerivedWithPrint()");
}

public void print() {
System.out.println("i = " + i);
}
}

这就是DerivedWithPrint类在运行时刻应该执行的命令及顺序。因为原来的DerivedWithPrint中也有print方法,所以覆盖了其父类的print方法。上述过程好像叫做类的扁平化。

注意,如果原DerivedWithPrint的print方法中有super.print()字样,那么扁平化之后的print方法应该这样:

public void print() {
System.out.println("BaseWithPrint.print");
System.out.println("i = " + i);
}

三,应该清楚了吧?
qingyuan18 2006-04-28
  • 打赏
  • 举报
回复
对了,i=47是 dp.print();打印出来的
qingyuan18 2006-04-28
  • 打赏
  • 举报
回复
DerivedWithPrint dp = new DerivedWithPrint();
//JVM检查到DerivedWithPrint有父类BaseWithPrint,于是将其一齐载入
//现在开始初始化,首先为成员变量分配空间,如果是基本数据类型将其初始化,因此这里i被初始化为0
//现在开始执行构造函数,首先执行父类构造函数 BaseWithPrint(),打印BaseWithPrint()
然后是print(),由于这时候new的对象是DerivedWithPrint,因此调用DerivedWithPrint overwrite的print方法 ,打印i = 0
//接下来是DerivedWithPrint类自己的构造函数,成员变量赋初值 ,i被赋为47,然后是DerivedWithPrint(){}中的方法,打印DerivedWithPrint() i = 47

捏造的信仰 2006-04-28
  • 打赏
  • 举报
回复
楼主,子类真的还没有构造吗?如果子类还没构造,那么谁来调用父类的构造函数呢?
syscoder 2006-04-28
  • 打赏
  • 举报
回复
to crazycy(崔毅):
类加载时不是首先初始化static的成员吗?
如果加载后成员函数就存在了(而与是否有这个类的实例无关),那么static的方法和non-static的方法有什么区别呢?
syscoder 2006-04-28
  • 打赏
  • 举报
回复
primitive不是指变量或对象吗?
方法也可以是primitive的?
killme2008 2006-04-28
  • 打赏
  • 举报
回复
this指针,其实每个非静态方法(也就是实例方法)都有个隐藏参数this, 通过此参数来区分到底调用的是哪个具体对象的方法....,而静态方法(也就是类方法)是没有此指针的
syscoder 2006-04-28
  • 打赏
  • 举报
回复
to UnAgain()
既然成员方法在内存中只有一份拷贝,那静态的方法和非静态的方法有什么区别呢?仅仅是在形式上静态方法可以由类调用而非静态方法不可以吗?
做鸡真好吃 2006-04-28
  • 打赏
  • 举报
回复
不存在和没执行是两个概念~
做鸡真好吃 2006-04-28
  • 打赏
  • 举报
回复
Mark~
seu_cose 2006-04-28
  • 打赏
  • 举报
回复
mark!
UnAgain 2006-04-28
  • 打赏
  • 举报
回复
类的成员方法,不管是不是static,在内存中只有一份拷贝。创建类的实例,即对象的时候,系统只会为其非静态的成员变量分配空间。
qingyuan18 2006-04-28
  • 打赏
  • 举报
回复
楼主不要用C的思维去套java,java里头没有一个用户栈的概念,所有的对象都分配在堆空间,java没有C/C++里头的delete销毁机制,它的垃圾收集是用垃圾收集器做的
killme2008 2006-04-28
  • 打赏
  • 举报
回复
实例方法和类方法都是共享的,区别就是实例有this指针

syscoder 2006-04-28
  • 打赏
  • 举报
回复
谢谢楼上UnAgain()老兄,关于子类、父类这块的调于明白了。还有一点疑问,我知道在C里面一个函数对应了一段可执行的代码,这些代码是存在内存中某段区域的,Java中是不是也是如此呢?如果是的话,那么类的成员函数(non-static)对应的那些内存区域到底是一个类共同拥有一段呢?还是每个对象各自拥有一段?
GALFORDD 2006-04-27
  • 打赏
  • 举报
回复
多肽的机制会自动查找这个方法是否被子类重写了,如果是的话就直接调用这个方法,和父类是否被初始化没关系的吧,因为这个方法在class里是存在的。类里的primitive变量是会被自动初始化的。
syscoder 2006-04-27
  • 打赏
  • 举报
回复
可能说得不是很清楚,输出结果:

BaseWithPrint()
i = 0
DerivedWithPrint()
i = 47

"i=47"没什么说的,我奇怪的是"i=0",此时DerivedWithPrint还没有被构造,Print()又不是Static型的,为什么就可以调用了呢?就好比一个对象还没有构造,如何能调用其成员呢?
crazycy 2006-04-27
  • 打赏
  • 举报
回复
类加载后成员函数就存在的
crazycy 2006-04-27
  • 打赏
  • 举报
回复
多态
GALFORDD 2006-04-27
  • 打赏
  • 举报
回复
这就是多肽的效果
加载更多回复(3)

62,615

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧