public class TestA1{
public TestA1(){              // 4
  System.out.println("P ");   //  5 打印出P
  this.init();                //  6,这里的this应该指的是TestB1 d,所以调用TestB1
}                             //的方法init() 
void init(){
  System.out.println("Q ");
}
public static void main(String argv[]){
  TestB1 d=  new TestB1();//程序从这里开始,1
}
}
class TestB1 extends TestA1{  
int i =1;
public TestB1(){  //  2
super();          //  3,调用基类的构造方法
System.out.println(i);  // 10 打印出 1
}
void init(){                 //  7
  System.out.println("C ");  //  8 打印出C
  i = 2;
  System.out.println(i +" ");  //9  打印出 2
}
}

解决方案 »

  1.   

    sea_man(渔夫)如果去掉了this程序打印出的结果还是原来那个啊
      我认为是TestB1中的
    init()重载了TestA1中
    init()
      

  2.   

    这道题是一道关于多形性的题。
    首先,TestB1继承TestA1,因此TestB1的void init()函数覆盖TestA1的void init()函数
    其次,在主函数中TestB1 d=  new TestB1();调用TestB1的构造函数,TestB1(),在TestB1中super()使得,TestB1调用他的父类TestA1的构造函数,TestA1(),因此
    首先执行System.out.println("P ");输出P
    再执行this.init();因此执行TestA1的init(),但是TestA1的init()已经被TestB1的init()重写,因此执行System.out.println("C ");
      i = 2;
      System.out.println(i +" ");
    输出
    C
    2
    最后再回到
    public TestB1(){
    super();
    System.out.println(i);
    }
    中,执行System.out.println(i);输出1 //此时i的值仍为1
      

  3.   

    to sea_man 
      为什么第四行“这里的this应该指的是TestB1 d,所以调用TestB1”
      

  4.   

    to sea_man(渔夫):和你探讨一下,在你的解释中说this指的是TestB1,我觉得不托,this关键子应该指向的是TestA1,而TestA1的init()被TestB1的init()重写,所以才调用TestB1的init(),你能不能再解释一下,谢谢!
      

  5.   

    这里涉及两个问题:
    1、构造器的调用顺序,构造器是按派生顺序从基类到子类的,
    2、方法覆盖中,当将子类句柄变量传递给基类对象时,所引用的句柄对象的类型决定执行哪个版本的函数。
    所在这个程序中;
    1、当创建一个TestB1对象时,首先调用TestA1的构造器,所以执行System.out.println("P ");  输出P 。
    2、然后执行TestA1的构造器中的this.init(),于是产生方法覆盖问题,由于引用的是TestB1对象,所以执行TestB1的init()函数,输出输出C和2。
    3、最后执行TestB1的构造器而执行System.out.println(i),输出1。
    4、在执行TestA1的构造器中this.init()时,还有一个造型问题,也就是将TestB1转化为TestA1,但实际上这时this是引用(或者指向)一个TestB1的句柄变量,所以执行TestB1的init()函数。
      

  6.   

    一句话,this 指向的是testbB1
      

  7.   

    这是多态在构造函数中一个值得注意的地方。 
    在构造函数中,要做尽量简单的动作,以避免你的程序存在漏洞。
    就上面的例子,当别人从你的类中继承来,他就可以让你的类执行到自己设计的代码,而不是你所愿意的。
    如果在构造函数中调用的是private的方法,那就不会有这些情况出现了。
    建议看看<thinking in Java 2/ed>与此相关的内容.
      

  8.   

    我认为ChrisZhang说的有道理:
    this是对当前对象引用的一个句柄变量(是不是可以认为是一个常量呢),这里只构造了TestB1的一个对象,所以this.init()应该是指TestB1的方法。
    至于去掉this之后仍然输出原结果,我想应该是方法重载的效果。
      

  9.   

    迷惑——
    我对下面程序用debug作了一下追踪调试,附有执行顺序。
    public class TestA1{
    public TestA1(){//3
       System.out.println("P ");//4
       this.init();//5
    }//10
    void init(){
       System.out.println("Q ");
    }
    public static void main(String argv[]){
       TestB1 d=  new TestB1();//1.......14
      }//15
    }
    class TestB1 extends TestA1{
    int i=10;//11
    public TestB1(){//2
    //super();
    System.out.println(i);//12
    }//13
    void init(){
       System.out.println("C ");//6
       i++;//7
       System.out.println(i +" ");//8
    }//9
    }
    执行结果为
    Q C 1 10
    我想问,既然i++在 int i=10之前执行,为什么没有出现i未被初始化的错误。
    由结果,我们也可以看出,在i++之前i是有值的为0,那么i是在什么时候被设定为整形且赋予初始值0的呢?关键是为什么!
      

  10.   

    楼上的,我的理解...
    执行程序时候,
    遇到TestB1 d = new TestB1();
    构建器申请内存把class TestB1 加载进来,并把内存区域擦干净,这是i(及其有关的成员变量)就是默认值0。
    接着,因为 'extends TestA1', 所以不再进行动作,先加载 TestA1,也一样TestA1此时设置默认值。为什么不先 初始化i = 10, 是考虑到可能这个初始化动作会受到父类初始化值的影响,所以要在父类初始化后再初始化子类的成员变量。
      

  11.   

    class T1 {
      String s = "T1";
      protected T1(String s) { 
        this.s = s + this.s;  //this
        this.msg(); //:如果这个this与上一行的this没什么不同,那么this就不是指向TestB1
      }
      void msg() { System.out.println(s); }
      T1 getT1() { return this; }
    }
    class T2 extends T1 {
      String s = super.s + "'s son";
      T2(String s) {
        super(s);
        this.msg();
      } 
      void msg() { System.out.println(s); } 
      T2 getT2() { return this; }
    }
    public class Test02 {
      public static void main(String[] args) {
        T2 t = new T2("hi ");
      }
    }
    输出:
    null
    hi T1's son似乎常见的this的作用就两个,如上例子,一是用来和方法那变量区分,一是 return this。
      

  12.   

    //xixi
    class T1 {
      String s = "T1";
      protected T1(String s) { 
        this.s = s + this.s;
        this.msg(); // call overrided msg(), print 'null'
        this.msg("");// call native msg(""), print 'hi T1'
      }
      void msg() { System.out.println(s); }
      private void msg(String trash) { System.out.println(s); }
    }
    class T2 extends T1 {
      String s = super.s + "'s son";
      T2(String s) {
        super(s);
        this.msg(); //print "hi T1's son"
        this.msg(""); //print "hi T1's son"
      } 
      void msg() { System.out.println(s); } 
      void msg(String trash) { System.out.println(s); } 
    }
    public class Test02 {
      public static void main(String[] args) {
        T2 t = new T2("hi ");
      }
    }
    结果:
    null
    hi T1
    hi T1's son
    hi T1's son
      

  13.   

    to:   alula(alula) 
    你这两段程序确实很好,他不仅说明了this的用法也说明了初始化顺序的问题。
    在think in java中是这样说明初始化顺序的:
    (1) 在采取其他任何操作之前,为对象分配的存储空间初始化成二进制零(或null)。
    (2) 调用基础类构建器。此时,被覆盖的msg()方法会得到调用(的确是在T2构建器调用之前),此时会发现s的值为null,这是由于步骤(1)造成的。
    (3) 按照原先声明的顺序调用成员初始化代码。
    (4) 调用衍生类构建器的主体。
    下面我再将你的第一个源程序运行顺序标示出来,互相比较,就可以看出其中的奥妙。
    class T1 {
      String s = "T1";                //4
      protected T1(String s) {        //3
        this.s = s + this.s;          //5
        this.msg();                   //6
      }                               //8  
      void msg() { System.out.println(s); }
      T1 getT1() { return this; }
    }
    class T2 extends T1 {            
      String s = super.s + "'s son";        //9
      T2(String s) {                       
        super(s);                          //2
        this.msg();                         //10
      }                                     //12
      void msg() { System.out.println(s); } //7.....//11
      T2 getT2() { return this; }
    }
    public class Test02 {
      public static void main(String[] args) {
        T2 t = new T2("hi ");                   //1......//13
      }                                        //14
    }
    至于this的含义,就只有一个指向当前类,
    this.s、this.msg();和return this;是三种用法,但是他们的含义是相同的,都是指向当前类。而对于this.msg();和this.s中的this完全可以不要。
      

  14.   

    要清楚的理解为什么输出结果,有以下两个关键点。
    1 java  中所有的成员方法都是 “虚函数”, 对象只调用真实对象中的对应方法。在本例子中super构造器调用的init方法实际上是调用TestB1的init方法。
    2 构造器中的执行顺序
     调用super构造器
     初始化成员变量
     执行构造器的其他部分 
      

  15.   

    请问zych72(闹闹) 
    关于:“java  中所有的成员方法都是 “虚函数””这种说法有没有详细的说明,我想看看,或者在《think in java》 中有没有,因为我只有这本书,
      

  16.   

    请问zych72(闹闹) 
    关于:“java  中所有的成员方法都是 “虚函数””这种说法有没有详细的说明,我想看看,或者在《think in java》 中有没有,因为我只有这本书,
      

  17.   

    to benediction(一颗神石):
    似乎java并没有要向程序员介绍"虚函数"概念的意思的。
    "虚函数"只是熟悉C++的同行在比较Java&C++时候的一种归纳挂。
    所以,你想详细"虚函数",可能需要看<thinking in c++>啦。呵呵和
      

  18.   

    关于虚函数你可以参考thinging in java 中的“多态”内容
    以下是sl-275中的部分内容
    Polymorphism
    &#8226; Polymorphism is the ability to have many different
    forms; for example, the Manager class has access to
    methods from Employee class.
    &#8226; An object has only one form.
    &#8226; A reference variable can refer to objects of different
    forms.Polymorphism
    Employee employee = new Manager(); // legal
    // Illegal attempt to assign Manager attribute
    employee. department = "Sales";
    // the variable is declared as an Employee type,
    // even though the Manager object has that attributeVirtual Method Invocation
    &#8226; Virtual method invocation:
    Employee e = new Manager();
    e. getDetails();
    &#8226; Compile- time type and runtime type
      

  19.   

    class C{
       static int j=5;
       int i=5;
    }我用SUN J2SE1。3的编译器做了一个试验,发现:
    (instance变量)i=5;是被编译进了构造器,放在构造器的开始部分.
    (static变量)j=5;是被编译进了一个“静态初始化块”另外,通过以前的帖子你会发现,静态函数和private函数不是“虚函数”,是“编译期绑定的”。