问题是这样的,先看一段代码 :
public class A {
public int num = 1 ;
public A() {
fun();
System.out.println("A constuctor.");
}
public void fun() {
System.out.println("A func." + num);
}
}
类 C:
public class C extends A {
public int nu = 100 ;
public static int no = 101 ;
public C() {
System.out.println("C constuctor.");
}
@Override
public void fun() {
System.out.println("C func." + nu + " --- " + no);
}
}
以上代码中,父类 A 在构造方法中调用了一个子类覆写的方法, 经过测试,在实例化 C 类对象时可以正常调用 C 类中 fun 方法 (覆写父类方法)
,只是 C 类成员属性没有初始化完成。
我想不明白,在 C 类没有构造完成的时候怎么可以调用对象方法呢? 目前我看不懂 JVM 原理,请知道这块的朋友帮我详细解释下,非常感谢!
测试代码就一行:
C c = new C() ;
非常感谢大家的回复,我大概知道了,谢谢! 就不一一回复大家啦! Thanks @All
1
fwrq41251 2016-02-15 16:42:26 +08:00
最好把测试代码贴一下
|
3
allenforrest 2016-02-15 17:01:37 +08:00
没看懂你的问题。。。
构造器里为啥不能调用成员方法? |
4
fwrq41251 2016-02-15 17:04:29 +08:00
java 里面构造方法会隐式的调用父类的无参构造方法(不管你写不写 super();这一句其实都一样).
静态成员或者静态块初始化和执行的时机是第一次使用到它们的时候. 没有直接回答 LZ 的问题,希望有所帮助.. |
5
okeydokey 2016-02-15 17:06:01 +08:00
类型信息在 jvm 启动阶段就已经写到方法区了,你 new 不 new 对象都可以调用,楼主最好搞清楚 java 内存模型,
|
6
raysonx 2016-02-15 17:08:00 +08:00
基类的构造函数是在派生类的构造函数之前调用的。基类的构造函数返回前,子类的构造函数还没有调用,成员无法初始化。
|
7
lusyoe 2016-02-15 17:10:55 +08:00
楼主的意思应该是实例还没生成怎么能调用到实例中的方法的。。不过有个误区不是类的构造函数执行完才生成对象的实例啊,在执行构造函数之前就有申请空间、静态初始化等一系列的操作,应该是在申请好空间后差不多实例就已经生成了,有了实例怎么就不能调用实例中的方法呢?你可以 Debug 一下看看
|
8
dullwit 2016-02-15 17:13:39 +08:00
Java 对象的实现化,是按照成员变量的声明顺序进行初始化,最后才是构造函数。当然在继承的状态下,是按照祖先链依次从基类进行初始化。
|
9
pelloz 2016-02-15 17:16:50 +08:00 1
楼主,你在将 class C 改成这样就能理解了:
public class C extends A { public int nu = 100 ; public static int no = 101 ; public C() { System.out.println("C constuctor."); } @Override public void fun() { nu = nu + 1000;//修改 nu 的值,你猜会怎样输出? System.out.println("C func." + nu + " --- " + no); } } 你在实例化 C 的时候,隐式调用 A 的构造函数, A 的构造函数调用了自己的 fun()方法,但是被子类覆盖后实际调用的是 C 的 fun 方法。但是 C 的 nu 还没有初始化, int 类型默认为 0 ,即使你在 fun()方法中修改 nu ,也会在接下来的 C 的初始化中被改回来。你认为 C 类没有构造完成,但实际上 C 已经被加载到内存并且各个域已经被赋了默认的初始值(不是你指定的初始值哦),当 C 的父类构造完以后才会使用你给的初始值完成实例化。以上基于个人理解,我也没有学到 JVM 原理.... |
10
asj 2016-02-15 17:25:07 +08:00
其实 Java 规范里有个几乎一模一样的例子:
http://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.5 Unlike C++, the Java programming language does not specify altered rules for method dispatch during the creation of a new class instance. If methods are invoked that are overridden in subclasses in the object being initialized, then these overriding methods are used, even before the new object is completely initialized. Example 12.5-2 和你的例子基本上是一样的。 |
11
raysonx 2016-02-15 17:26:49 +08:00
@pelloz +1 好例子。
Java 会在分配内存之后(但在初始化之前)对内存用 0 填充。 补充:如果换成 C++的话,成员初始你之前是之前内存的垃圾数据,你会得到一个很古怪的值。 C++编译器在 Release 编译模式下不会对分配的内容做填充。 |
13
KentY 2016-02-15 17:45:36 +08:00
当你 C c = new C();的时候, 一个 C object 已经有了, 只是所有 fields (非 static) 都是默认值, 所有 object reference fields 都是 null, method references 也已经创建了. 即使你声明 fields 时候有赋值语句, 也不被执行. 当这个准备工作完成以后, 才调用 constructor, 以及 superclass.constructor(). 所以, 到了这步是可以找到相关的 method reference 的.
|
14
palmers OP @pelloz 嗯嗯 你解释的很对,只是我一直以为,对象没有构造完成,对象方法也不能使用,通过大家的回复,我这个结论大多是错了。 非常感谢!
|
15
palmers OP @pelloz `即使你在 fun()方法中修改 nu ,也会在接下来的 C 的初始化中被改回来。` 这句话我觉得使用引用类型更有说服力。
|
16
mfaner 2016-02-15 17:57:15 +08:00
这种在 NetBeans 会出警告的: Overridable Method Call in Constructor
|
17
zonghua 2016-02-15 18:27:54 +08:00 via iPhone
@pelloz 不明白,为什么默认调用 A 的构造方法,然后调用的又是 C 重写的 fun 方法。不是 A 去调用吗?
|
21
palmers OP @zonghua 这里 C 继承 A 初始化 C 之前需要先初始化 A, 因为 C 默认显式继承了 A 的部分信息,如果 A 没有初始化,则 A 的初始化信息 C 也就拿不到,严格上 C 是不能使用的。所以 A 需要得到初始化。 所以在默认情况下 C 初始化之前会隐式调用父类默认构造方法,保证父类(基类)能在子类初始化之前得到初始化。
关于在 A 构造方法中调用了子类方法,其实是因为多态特性, JVM 发现 fun 方法被子类覆写,则调用了子类 fun 方法,实现方法多态,这个我觉得是因为当前类型信息为 C 所以才会调用 fun 的。 以上是我的理解,你可以参考,但不一定正确。 |