class Depend
{
int i = 10;
public Depend()
{
print();
i = 20;
}
void print()
{
System.out.println("Depend=> " + this.i);
}
}public class Target extends Depend
{
int i = 30;
public Target()
{
print();
super.print();
i = 40;
}
void print()
{
System.out.println("Target=> " + i);
}
public static void main(String[] args)
{
Target target = new Target();
}
}其执行结果为什么是:Target=>0
                 Target=>30
                 Target=>20

解决方案 »

  1.   

    困惑《JAVA编程思想》上说:“class中初始化次序取决于变量在class中的定义次序。变量定义也许会散落各处,而且有可能借与各个函数定义之间。但所有变量一定会在任何一个函数(甚至是构造函数)被条用之前完成初始化。”上面的代码中,明明在定义int i = 10的时候就初始化i = 10; 为什么首先父类的构造方法调用print() 方法的时候,i=0呢?请高手指点....
      

  2.   

    public Depend() 

    print(); 
    i = 20; 
    } 父类中的print()方法是调用的子类的print()方法,而此时子类的成员变量还未赋值,所以为0.
      

  3.   

    这里面有个问题需要说明
    构造方法会优先于属性初始化,因为构造代表着分配内存之类的基础功能,只有构造好了,其他的才能使用
    那么问题在于Target里面的
    public Target() 
    {  
      print(); 
      super.print(); 
      i = 40; 

    在第一个print()之前,会优先调用父类的构造器,而父类的构造器里面的print()方法,被override了,实际调用的是子类的方法,可惜,此时子类还没有完成初始化,因为父类的构造器还没有返回呢。 所以其属性全部为默认值。 数字当然就是0啦!
      

  4.   

    上述老大们说什么构造方法优先于属性初始化是错误的
    属性初始化应该优先于构造方法,请看下列代码,刚刚自己写的^_^class Father{
    int i = f();
    int f(){
    System.out.println("in Father's f()");
    return 1;
    }
    Father(){
    System.out.println("in Father's Father()");
    }
    }public class Son extends Father{
    int i = g();
    int g(){
    System.out.println("in Son's g()");
    return 1;
    }
    Son(){
    System.out.println("in Son's Son()");
    }
    public static void main(String[] args){
    Son s = new Son();
    }
    }运行结果是:
    in Father's f()
    in Father's Father()
    in Son's g()
    in Son's Son()
      

  5.   

    因为变量不是静态的!如果你把变量改成静态的你就可以得到你想的结果了!
    如:
    static int i = 10;
    所有变量都改成以上的形式就可以了!
      

  6.   

    我补充一下我在7楼说的内容,用实际代码,^_^
    class Father{
    static int j = f1();
    int i = f();
    static{
    System.out.println("父类静态方法体");
    }
    {
    System.out.println("父类非静态方法体");
    }
    static int f1(){
    System.out.println("父类静态属性");
    return 1;
    }
    int f(){
    System.out.println("父类非静态属性");
    return 1;
    }
    Father(){
    System.out.println("父类构造函数");
    }
    }public class Son extends Father{
    static int j = g1();
    int i = g();

    static{
    System.out.println("子类静态方法体");
    }
    {
    System.out.println("子类非静态方法体");
    }

    static int g1(){
    System.out.println("子类静态属性");
    return 1;
    }
    int g(){
    System.out.println("子类非静态属性");
    return 1;
    }
    Son(){
    System.out.println("子类构造函数");
    }
    public static void main(String[] args){
    Son s = new Son();
    }
    }lz,按我的初始化顺序跑跑看,你就不会疑惑了,呵呵
      

  7.   


    public class Depend {
    int i = 10;//第4步 public Depend() {//第3步
    print();//第5步
    i = 20;//第7步
    } void print() {
    System.out.println("Depend=> " + this.i);//第12步
    }
    }public class Target extends Depend {
    int i = 30;//第8步 public Target() {//第2步
    print();//第9步
    super.print();//第11步
    i = 40;//第13步
    } void print() {
    System.out.println("Target=> " + i);//第6步,这时子类中的i还为0   第10步  i=30
    } public static void main(String[] args) {
    Target target = new Target();         //第1步
    }
    }用断点跟踪一下,就可以看清了。
    6楼说的对,构造方法优先于属性初始化是错误的。
    而且子类中的i和父类中的i是两个变量。
      

  8.   

    to:duqi_1985这么清楚还看不出来啊-_-o
      

  9.   

    呵呵!如果属性优先于构造器,那么为什么子类的属性会晚于其构造器呢?那应该先把属性设置好了,再调用构造器啊!也就是,所谓的第二步,应该先调用 int i=30; 为什么先到 public Target() 这里干什么?我想你们曲解了我的意思。我说的优先,并不是里面的代码,而是类初始化,先到构造器这里来执行,然后调用父类的构造器,直到父类构造完毕,才会调用本类的属性,然后运行剩余的构造代码。
      

  10.   

    to: java2000_net关于你说的第2步是jon_wd给出的吧,我不是很清楚
    你看看我在10楼的代码吧^_^
      

  11.   

    哦~~我知道java2000_net的意思了
      

  12.   

    我觉得debug在构造方法名一行上停留一步比较值得探讨
    个人觉得这只是debugger定位所要构造类的一个标识定位中转而已
    不存在顺序上的混淆
      

  13.   

    123public class Depend {
        int i = 10;//第4步    public Depend() {//第3步
            print();//第5步
            i = 20;//第7步
        }    void print() {
            System.out.println("Depend=> " + this.i);//第12步
        }
    }public class Target extends Depend {
        int i = 30;//第8步    public Target() {//第2步
            print();//第9步
            super.print();//第11步
            i = 40;//第13步
        }    void print() {
            System.out.println("Target=> " + i);//第6步,这时子类中的i还为0   第10步  i=30
        }    public static void main(String[] args) {
            Target target = new Target();         //第1步
        }
    }点解第5步会跳到第六步去????
      

  14.   


    你没写错吧,应该是下面的结果呀,
    Target=> 0
    因为print被Traget重载,所以在Depend初始化时调用Traget的print方法,但此时Traget还没有被初始化所以默认为0
    Target=> 30
    因为先初始化属性值Target构造函数被调用前i=30了,所以输出30
    Depend=> 20
    Depend后来被设为20所以输出是正确的
      

  15.   


    结论是对的,但是理由说得不充分。根据class的文件定义(下面这个链接)
    http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html#80959
    class里面根本就根本不可能有【int i = 10;】这样的语句。
    在看看javac的源代码就知道,比方说下面的代码。public class Test {
    int i=1;
    int j;
    public Test(){
    j = 1;
    i = 2;
    }
    }在class里面被搞成下面这个样(用jad反编译就知道):public class Test
    {    public Test()
        {
            i = 1;
            j = 1;
            i = 2;
        }    int i;
        int j;
    }所以属性初始化都被放到缺省构造函数中了,这样的话,执行顺序都应该可以搞明白了。
      

  16.   


    因为基类的print()方法已经被覆盖,所以基类构造器中的print()方法执行的是子类中的print()方法!
      

  17.   

    Target=> 0
    Target=> 30
    Depend=> 20
    -----------
    LZ你DEBUG一下就很清楚了.class Depend {
    int i = 10;//1 public Depend() {//2
    print();//这里调用的是Target的print(),此时Target的i未初始化,所以是0.
    i = 20;
    } void print() {
    System.out.println("Depend=> " + this.i);
    }
    }public class Target extends Depend {
    int i = 30;//3 public Target() {//4
    print();//这里调用的是Target的print(),此时Target的i已初始化,所以是30
    super.print();//很明显调用Depend的i,是20
    i = 40;
    } void print() {
    System.out.println("Target=> " + i);
    } public static void main(String[] args) {
    Target target = new Target();
    }
    }
      

  18.   

    因为基类的print()方法已经被覆盖,所以基类构造器中的print()方法执行的是子类中的print()方法!
    而此时子类中的成员变量还没有被初始化,默认值为0,所以第一个输出的结果为0。
      

  19.   

    用debug跟一下就知道了 
    好经典的一道题目
      

  20.   

    java2000_net 说的很仔细了。
      

  21.   

    //class Depend
    //{
    //int i = 10;
    //public Depend()
    //{
    //print();
    //i = 20;
    //}
    //void print()
    //{
    //System.out.println("Depend=> " + this.i);
    //}
    //}//public class Target extends Depend
    public class Target {
    // int n = 30;
    static int n = 30;   //第一步 int i = 19;   //第四步 public Target()  //第三步
                 {
    System.out.println("n=" + n); //  第5步
    System.out.println("i=" + i);//  第6步
    print();                     //第7步

    i = 40;                     //第10步
    } public static void print() {
    System.out.println("hello");   //第8步   第13步
    System.out.println("Target=> " + n);   //第9步   第14步
    } public static void main(String[] args) {
    Target target = new Target();  //第二步
    System.out.println("hello");       //第11步

    target.print();  //第12步
    }
    }
    用eclipse调试功能获得步骤
    那个public static void print()  静态方法不是在构造函数之前调用,这是什么原因呢?
      

  22.   

    为什么在父类调用print()方法是重写后的不是父类自己的?
      

  23.   

    大家可以尝试一下将子类Target的int i = 30 注释掉,会有很意外的收获哦。
      

  24.   

    看了以上的内容,我有如下几点理解:一、一个类的初始化是从创建基类Object的对象开始,根据继承链依次创建其父类对象,最后才是该类本身。二、在初始化过程中,如果构造器调用某个方法,而该方法已被子类重写(覆盖),则使用子类的方法。不知道这样理解对不对。
      

  25.   

    "我想你们曲解了我的意思。我说的优先,并不是里面的代码,而是类初始化,先到构造器这里来执行,然后调用父类的构造器,直到父类构造完毕,才会调用本类的属性,然后运行剩余的构造代码。"
    是正解
    同时一任天然★IT民兵的很有道理,自己打个断点debug一遍,一切就清楚了
      

  26.   

    在编译后的文件中本类中的全局变量放在了构造函数中。(源自chen09)类初始化,先到构造器这里来执行,然后调用父类的构造器,直到父类构造完毕,才会调用本类的属性,然后运行剩余的构造代码。(源自java2000_net)可以知道:
    在执行父类中构造函数中语句i=20,而后执行print()时,因此方法被子类OVERRIDE,
    所以动态绑定的原因,实际上执行的是子类的方法,而此时子类并没有初始化完成,所以默认赋值i=0。输出Target=> 0;而后执行子类中构造函数中赋值语句i=30,再执行print()方法。输出Target=> 30;而后执行super.print()语句,指明执行父类中print()方法。输出Depend=> 20。总结完毕。撤!
      

  27.   


    我做了一下测试,把Target类的print()函数改名,也就是不override,输出结果为
    Depend=>10
    Target=>30
    Depend=>20
    这就验证了5楼的说法,也就是:在父类的构造函数里,如果调用到的函数子类有重写,调用的则是子类重写的函数.
    所以我觉得5楼的说法是正确的.
      

  28.   

    我想说一下关于类的属性和构造函数初始化的谁先谁后的问题,
    以下是一个简单的例子
    public class Test  {
    private int a = 2;
    private int b;
    private int c = 1;
    public Test(){
    a = 3;
    }
    public void print(){
    System.out.println("a=" + a);
    System.out.println("b=" + b);
    System.out.println("c=" + c);
    }
    public static void main(String[]args){
    Test t = new Test();
    t.print();
    }
    }
    执行结果:
    a=3
    b=0
    c=1
    ----------------------
    b=0,c=1说明了设初始值生效
    而a的初始值本为2的,但最终由于构造函数的初始化工作,把a最终初始为3了.
    所以,属性先于构造函数初始化,但属性最后的初始化由构造函数决定.
      

  29.   

    不管怎么说  这个问题问的好   让我对继承和JVM虚拟机的加载过程有了更深的理解   学习了 呵呵~~~
      

  30.   

    没有发现吗,在超类和子类里面都有个print方法,但在第5步里调用的是子类的print方法,所以第一个值是0。
      

  31.   


    你说的对,是赋值,我说得不清楚.
    我解释一下
    在<<core java>>第七版中文版的第一卷的第104页中有这么一句"构造器被运行,并用于将实例域初始化为所希望的状态".
    可以理解为,构造函数是为初始化工作而存在的吧?
    结合你的意思,在构造函数里,是通过赋值来完成最后的初始化工作,这样说对吗?
    在类初始化工作的总过程中,我把构造函数的工作理解作最后的初始化工作,不知这个对不对了,我自己想当然而已,希望有人给个确切的答案.