一个java学习中的测试程序,我搞不懂了,关于集继承的
package test1;/**
 *
 * @author leslie
 */class MyA {
int a = 1;
public MyA() {
print();
} public void print() {
System.out.println("aaa" + a);
}
}class MyB extends MyA {
int a = 2;
public MyB() {
print();
} public void print() {
System.out.println("bbb" + a);
}
}class MyC extends MyB {
int a = 3;
public MyC() {
print();
} public void print() {
System.out.println("ccc" + a);
}
}public class Main {    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
       
new MyC();

}
}
这个程序在netbean下返回的是:
ccc0
ccc0
ccc3
不知道为什么?那位能给我解释下为什么会这样啊?我只调用了一次为什么3个结果?
而且a没有赋值为0啊?谢谢谢谢

解决方案 »

  1.   

    class MyA { 
    int a = 1; 
    public MyA() { 
     print(); 
    }  public void print() { 
    System.out.println("aaa" + a); 


    ///
    class MyB extends MyA { 
    int a = 2; 
    public MyB() { 
    print(); 
    }  public void print() { 
    System.out.println("bbb" + a); 

    } class MyC extends MyB { 
      int a = 3; 
      public MyC() { 
    print(); 
    }  public void print() { 
    System.out.println("ccc" + a); 


    public class text {     /** 
        * @param args the command line arguments 
        */ 
        public static void main(String[] args) { 
          
     new MyC();
     //当你new一个子类的时候,它会自动调用父类的构造函数,依次向上调用,先调用 MyA (),然后MyB(),最后是MyC(),所以会出现三个结果.
     //没有出现三,是因为你的MyC()的构造函数没有打印的.


    /*改成这样的……
    **
    */class MyC extends MyB { 
      int a = 3; 
      public MyC() { 
    System.out.println("ccc" + a); 
    } }
    [你的代码我给你缩进了]
    提个建议:(比算法还重要)1.大括号对齐.
    2.遇到左大括号缩进(简单的Table键缩进).
    3.方法和方法之间,方法和属性之间,一些重要的语句之间用空格隔开.
    4.并列语句之间加空格. Name MyName = new Name();
    5.运算符两侧加空格. int i = 8;
    6.成对编程. 写完{后马上写上}.
    目的:要让别让看得清楚,要让别人看的舒服.
      

  2.   

    我认为是这样的:
    在调用MyC构造方法之前,先调用父类的无参构造方法(如果父类没有无参构造方法就报错)
    此时虽然new还没返回,但this已经有了,由于动态绑定,每次调用父类的构造方法时执行的print()
    都是MyC中的print().
    在执行父类构造方法时还没执行了a = 3;所以a一直是0(变量没有动态绑定)
    一直到把父类构造方法都执行完了
    执行a = 3;
    在调print()就打印出3了
      

  3.   


    楼上的是否想这样??class MyC extends MyB { 
      int a = 3; 
      public MyC() { 
    System.out.println("ccc" + a); 
    } } 
      

  4.   

    刚才没有看清你的程序(没有缩进的缘故,还有本人比较搓),以为你的print()是系统的方法,还查了API
    呵呵……
    对构造函数的调用,先调用超类的,然后依次向下,仅仅调用构造函数,对你的print()视而不见.
    对此完全赞成二楼的.你可以稍微改动几处看看效果.
    1.把MyA的构造函数改一下,就看清了.
     public MyA{
      System.out.println("aaa"+a);
    }2.你可以对MyC的print()方法改一下,看一下输出,就明白了.
    class MyC extends MyB { 
    int a = 3; 
    public MyC() { 
    print(); 
    } public void print() { 
    System.out.println("ccc+abcccccc" + a); ///////看看输出


      

  5.   

    执行new MyC();时候的调用顺序.
    1. ClassLoader加载MyC这个类.
    2. 准备调用MyC的构造方法.
    3. 准备调用MyC的父类MyB的构造方法.
    4. 准备调用MyB的父类MyA的构造方法.
    5. 准备调用MyA的父类java.lang.Object的构造方法.
    6. 执行java.lang.Object的构造方法.
    7. 初始化MyA类的成员变量,a=1;
        注意:此时堆栈中对象的分布是MyC的对象持有MyB对象的一个引用,MyB对象持有MyA对象的一个引用,MyA对象持有java.lang.Object对象的一个引用,MyA,MyB,MyC对象中各有一个成员变量a,一定注意,这个时候,堆栈中有三个a,此时MyA的成员变量a=1;MyB和MyC的成员变量a=0;
    8. 执行MyA的构造方法,调用print方法   
       注意:这里有多态,我们调用的方法实际上是MyC对象重写的方法,也就是说内存代码区向外提供调用的print方法是MyC的print方法,由于MyC中的成员变量a=0,所以此时打印"ooo0";
    9. 初始化MyB的成员变量.和第7条同理,此时堆栈中MyA的a=1,MyB的a=2,MyC的a=0;
    10. 执行MyB的构造方法.和8同理,调用的还是MyC的print方法,所以打印的是"ooo0";
    11. 初始化MyC的成员变量.和第7条同理,此时堆栈中MyA的a=1,MyB的a=2,MyC的a=3;
    12. 执行MyC的构造方法.和8同理,调用的是MyC的print方法,此时MyC的成员变量a=3,所以打印的是"ooo3";做这个内存分析的时候,主要考虑new MyC();这一句执行的过程:
    1. ClassLoader加载MyC;
    2. 进入MyC的构造器,首先构造MyC的父类,直到Object,Object之后怎么处理就不清楚了.
    3. 处理完父类构造器之后,处理成员变量的初始化.
    4. 然后执行构造器中的代码.相关的东西:
    多态:一个类继承关系中的重写的方法,在调用的时候,只有一个,那就是重写了那个方法的备份最小的类中的方法体.
      

  6.   

    楼上的解释有点不明白,只调用父类的构造函数,构造函数会给所有实例变量赋初值的,因为MyA中有int a=1  会给a=1的,我觉得6楼的解释比较正确
      

  7.   

    package test1; 
    //java继承中,要稿清楚三个问题: 
    //1.子类覆盖与父类同名的方法,即父类同名方法在子类中不可见
    //2.子类隐藏父类同名的属性,即父类属性是可见的,但是它的值会变成默认值,并且得有父类的对象或方法调用
    //3.构造子类对象时,默认调用父类的无参构造//打印3条是因为子类默认调用父类无参构造打印
    //打印0是因为和子类属性同名,变成了默认值0
    //详解见下
    class MyA {
    int a = 1;
    public MyA() {
    print();
    }
    public void print() {
    System.out.println("aaa" + a);
    }
    }class MyB extends MyA {
    int a = 2;
    public MyB() {
    print();
    }
    public void print() {
    System.out.println("bbb" + a);
    }
    }
    //MyC类分别继承了MyB,MyA的属性a和print方法,但是由于它们都和MyC中的属性和方法同名,
    //print方法被覆盖,a被隐藏,并且值编程默认值0
    class MyC extends MyB {
    int a = 3;
    public MyC() { //2)做print();之前默认调用MyB(),MyB()又默认调用MyA(),
             //3)调用父类构造时,做的是子类的print(),打印的a是自己的,不过,对于父类的是默认值0,子类的还是原始只
    print();
    }
    public void print()  {
    System.out.println("ccc" + a);
    }
    }public class Main {
        public static void main(String[] args) {
    new MyC();// 1).调用构造MyC();
        }

      

  8.   

    class MyA{
    int a = 1;
    MyA(){
    printA();
    }
    void printA(){
    System.out.println("MyA"+a);
    }
    }class MyB extends MyA{
    int a=2;
    MyB(){
    printB();
    }
    void printB(){  //重写了MyA类的print()方法
    System.out.println("MyB"+a);
    }

    }class MyC extends MyB{
    int a = 3 ;
    MyC(){
    printC();
    }
    void printC(){    //重写了MyB类的print()方法
    System.out.println("MyC"+a);
    }

    }
    //////
    public class MyClass{
    public static void main(String[] args){
    new MyC();
    }
    }说明几下:再重复一下,先调用超类的构造函数,所以就调用MyA 类,而MyB类的print()
    方法覆盖了超类MyA类的print()方法;MyC类雷同……
    所以,当你New Myc的时候,总是调用自己的print()方法.
    为什么刚开始两个是0呢?那是因为程序先执行的是new MyC();还没有执行int a = 3;
    也就是执行完了两个超类的构造函数后,才执行了自己的构造函数,所以最后一个打出来的
    是3.我上面的程序取消了子类重写父类的print方法,所以结果正是Lz期望的.结果为:
    MyA1
    MyB2
    MyC3