public abstract class Demo { public Demo() { this.print(); } public abstract void print(); } public class DemoImple extends Demo{ private int x = 10; public DemoImple(int x) { this.x = x; } public void print() { System.out.println("x=" + this.x); } } public class CaseDemo09 { public static void main(String[] args) { new DemoImple(100); } }
这个case里面,最后的结果是:x=0
给出的解释是:通过子类对象来进行实例化。
不清楚的地方:
1、 new DemoImple(100); //这里不是传递了数值100吗?为什么会是0呢 ?
2、之所以有输出,是不是调用了父类,即抽象类Demo的构造方法?实际调用的子类的print() 方法?中间调用机制或是过程是怎样的?
烦请大牛路过解释一下,谢谢了O(∩_∩)O~
鉴于有同学用C#测试了,结果为10,我以为:
1、new DemoImple(100); 这句首先调用了父类的构造方法 Demo(),由于多态,Demo 中实际调用的是子类 DemoImple 中重写过的 print() 方法,而此时 this.x 值为 DemoImple 中的 private int x=10,所以输出为:x=10
2、new DemoImple(100);中将 100 赋值给 this.x 但是并没有输出。
不知道以上对 c# 理解正不正确. 至于 Java ,x = 0,待大牛解答一下..
用java做了下试验,说一下我得到的结论,不过我也不是很确定:
调用子类的构造器的时候,首先会调用父类构造器,所以这个时候x的值还没有变成100;
然后,为什么不是10而是0呢,我在子类构造器中显式调用了super()来调用父类构造器,然后在这个地方断点,发现在调用父类构造器的时候x的值是0,而不是10,所以这个时候我觉得成员变量还没有初始化(这个就是我说的不确定的地方,虽然试验结果证明了我的说法,但没理解原理),
我把我测试的代码贴给你,你拿去测试试一试,
package abstractClass; abstract class AbstractDemo { { System.out.println("execute super block"); } static{ System.out.println("execute static super block"); } public AbstractDemo(){ System.out.println("call super constractor"); this.print(); } public abstract void print(); } class DemoImpl extends AbstractDemo{ { System.out.println("execute block"); } static{ System.out.println("execute static block"); } private int x = 10; public DemoImpl(int x){ super(); //断点 System.out.println("call constractor"); System.out.println("before init in constractor this.x="+this.x); this.x = x; } @Override public void print(){ System.out.println("this.x="+x); } } public class TestAbstractDemo{ public static void main(String[] args) { new DemoImpl(100); } }
结果如下:
execute static super block
execute static block
execute super block
call super constractor
this.x=0
execute block
call constractor
before init in constractor this.x=10
刚刚又试了一下,执行完super()之后,执行了初始化this.x,此时this.x才变成了10,然后才执行构造器。
我把结论写了个博客,感兴趣的话可以看看,顺便帮我看看有没有什么问题,:)
最后结果应该是x=10才对吧。你这是Java吗?Java我不是很了解,反正对于C#来说,x=10,因为首先首先被调用的是抽象类的构造函数,然后这个时候x还是10,当print后才调用DemoImpl的构造函数,这个时候虽然将x设置为100,但是已经没有print了。
是Java,不过Java 运行结果确实是 x=0,C#环境下,你运行了吗?
@疯子也疯狂: 呃,以我对C#的了解,不用运行我也知道肯定是10啊。
public class DemoImple extends Demo{
private int x = 10;
public DemoImple(int x)
{
this.x = x;
}
public void print() {
System.out.println("x=" + this.x);
}
}
在子类中没有重写抽象类的 print的方法 java中是否有重写关键字??
java 中函数重写,只要将函数体重写就可以了,没有关键字.源程序中已经进行了重写了.
@疯子也疯狂: 我刚才用c#测试了 结果是10
@wolfy: 谢谢帮忙用 c# 测试.
这个和类的加载机制有关,在构造子类对象new DemoImpl(100)时,先调用父类的构造方法public Demo() { this.print(); },根据java的运行时多态性,子类对实现了父类的print()方法,此时调用子类的print();
由于构造子类时会先构造父类;而构造父类时,其所用的静态成员和非静态属性是父类的,但非静态方法却是子类的;
由于构造父类时,子类并未加载;如果此时所调用的非静态方法里有成员,则这个成员是子类的,且非静态属性是默认初始值的。
这里由于子类的x属性并未加载,初始值为0.
要理解这个问题需要清楚类的加载顺序,可以参考这篇文章:http://hi.baidu.com/linyongboole/item/74c32815dbe5c10fd1d66d04
学习了.
实例化DemoImple时,执行顺序是:
1、先执行父类的构造方法: public Demo(),里面有print();而现在x还没有值,所以打印“0”;
2、执行本类的构造方法:public DemoImple(int x);为本类中的x赋值为100;此时x为100;但是你没有打印!
public DemoImple(int x)
{
this.x = x;
this.print();
}
这样就看到打印的100了