class Animal{
String color="red";

Animal(){
print();
color="black";
}

void print(){
System.out.println("animal color is "+color);
}
}class Cat extends Animal{
String color="yellow";

Cat(){
print();
super.print();
}

void print(){
System.out.println("cat color is "+color);
}

public static void main(String[] args){
new Cat();
}
}结果是:
cat color is null
cat color is yellow
animal color is black
疑问:
1.当子类构造函数去调用父类构造函数时,如果父类构造函数中调用的成员函数被子类覆盖过,那么就用子类?
2.为什么是null?  显示初始化不是优先于构造函数么?当构造函数启动时,成员变量不是已经被显示初始化了么?

解决方案 »

  1.   

    1.是的
    2.new Cat() 先调用父类构造方法 调print()方法 所以null 
      

  2.   

    1.根据打印出来的结果,可以肯定是这样的。
    2.之所以是null是因为当父类调用子类的print方法时,子类的color变量还没有执行初始化。显示初始化时先于构造函数没错,但是指的是当前的类的变量的初始化,楼主给出的例子中,JVM首先初始化了父类的color变量,然后调用了子类的print方法,但是此时还没有初始化子类的color变量。而子类的方法调用的是子类的color变量,所以打印出来的就是null了。当构造函数启动时,成员变量是被加载,而不是初始化,也就是说这个指针存在,但是这个指针不指向任何对象。
      

  3.   

    LZ好好理解一下以下的例子就知道了
    class A {
        {System.out.println("执行顺序1");} //这里叫做初始化处理块
        public A() {
            System.out.println("执行顺序2");
        }
    }class B extends A {
        {System.out.println("执行顺序3");} //初始化处理块,String color = "yellow"; 相当于
                               //String color; //声明变量,相当于给变量分配内存空间并初始化为缺省值
                           //{color = "yellow"} //在初始化块给变量赋值
                              //所以,从执行顺序来看,LZ应该知道为什么是null了吧
        public B() {
            System.out.println("执行顺序4");
        }
    }public class C {
        public static void main(Srting[] args) {
            new B();
        }
    }
    new 子类()的执行顺序是
    1 父类初始化块
    2 父类构造方法
    3 子类初始化块
    4 子类构造方法LZ再好好体会一下吧
      

  4.   

    1.至于为什么会调用子类的print,那是因为你创建的是子类这个对象,在类中直接调用print()相当于this.print(),然而当前这个对象是Cat所以就调用了子类的print方法。如果你new的父类,那么自然调用的就是父类的print方法咯。
    2.为什么会是null?这个很明显,对象是先初始化然后复制,虽然String color在构造函数之前,但是他的复制却是要等待对象初始化完成之后。而对象创建是构造函数执行完成之后,所以你在构造函数中调用color属性,而这个时候color是还没有赋值的。那么String的初始默认为null。所以也就打印null咯
      

  5.   


    有点小疑问:既然赋值在初始化之后,可以解释子类构造函数中super()那句的打印结果,但为什么下一行print()打印的结果有值。
      

  6.   

    多谢的你的回答,很不想为这样的问题浪费那么多时间,说不定这辈子也就遇到这一次了,但还是想了解。。:
    能否这样理解,当jvm执行到子类构造函数的super()之前,已经做好了什么事?真是太痛苦了遇到”先有鸡还是先有蛋“这样的问题。我想这是大家公认的结果吧:先初始化父类的静态代码--->初始化子类的静态代码-->初始化父类的非静态代码--->初始化父类构造函数--->初始化子类非静态代码--->初始化子类构造函数
    也就是说当到了子类构造函数时,前面的事都完了。。  又回到这个问题的起点,我崩溃了- -!!
      

  7.   

    首先,这不是先有鸡还是先有蛋的问题,这个问题有非常明确的答案,然后,下面这段代码,你自己在Eclipse里执行一下吧!问题就迎刃而解了。呵呵!class Animal{
    static{
    System.out.println("Static Animal....");
    }
    {
    System.out.println("Animal....");
    }
        public String color="red";
        
        Animal(){
            print();
            color="black";
        }
        
        void print(){
            System.out.println("animal color is "+color);
        }
    }public class Cat extends Animal{
    static{
    System.out.println("Static Cat.....");
    }
    {
    System.out.println("Cat...");
    }
        public String color="yellow";
        
        Cat(){
            print();
            super.print();
        }
        
        void print(){
            System.out.println("cat color is "+color);
        }
        
        public static void main(String[] args){
            new Cat();
        }
    }
      

  8.   

    你首先要明白的是,从根本上来讲,对于子类来说,他包含了父类的所有方法,即便是他覆盖的方法他也是包含了父类的和子类自己的。所以,这个内存空间当然是包含了所有子类的和父类需要的空间。而且,当你只创建子类对象时,不存在还要创建父类对象这个说法。你可以在父类的构造函数中打印一下this指针,在子类中也打印一下,你会发现,this指的是同一个对象。