发现了一个问题,愿意将我的思考过程分享出来,也希望有高手能解答我的疑惑。谢谢。情境一:
   * ParentClass.java
     
     /**
      * 超类
      */
      public class ParentClass {

  public ParentClass(String str1, String str2) {

  }       }
     
     
   * Subclass.java
     
     /**
      * 子类,继承了ParentClass类
      */
      (1) public class Subclass extends ParentClass {         }
     
     
   * 异常:
       在“(1)”标记处编译器提示:Implicit super constructor ParentClass() is undefined for default constructor. Must define an explicit constructor   * 异常描述:我大致想了一下,可能冲突的地方时这样——因为父类(ParentClass)有一个带参的构造器,并且,构造器的参数一般比较重要,将在上下文中继续使用。也就是说 ParentClass 的其它方法将很可能使用到这个构造器的两个参数。而,按照继承规则,子类的对象将可以使用到父类的方法,所以,ParentClass中 那些使用了构造器参数值的方法,其子类(Subclass)对象将可能访问到。因此,必须要确保父类构造器的参数可以被赋值。“可以被赋值”有两种方式,一是先实例化出对象,再通过setter赋值;二是在实例化的时候就赋值,即通过构造器参数的形式传参。
     上例中,父类(ParentClass)没有提供默认的无参构造器。所以,方式一被封死。而方式二在本例中,无法通过在实例化Subclass时,同时为父类构造器变量赋值,方式二亦被封死。聪明的编译器意识到了这个风险,于是提前标识错误。
     根据这个思考,我给父类(ParentClass)加上了一个无参构造器,编译错误消失:
     * ParentClass.java
     
     /**
      * 超类
      */
      public class ParentClass {
  (2) public ParentClass(){ }
  public ParentClass(String str1, String str2) {

  }       }
     
     “(2)”处为新加入的语句。      继续思考,既然有两种解决方式,那么第二种方式该如何实现。我的理解是让子类覆盖父类的带参构造器:
       * ParentClass.java
     
     /**
      * 超类
      */
      public class ParentClass {

  public ParentClass(String str1, String str2) {

  }       }
     
     
   * Subclass.java
     
     /**
      * 子类,继承了ParentClass类
      */
      public class Subclass extends ParentClass {
       (3) public Subclass(String str1, String str2) {
(4) super(str1, str2);
}    
      }
     
     “(3)”处的方法为新添。语句“(4)”不可以删去,可以解释为保障父类的构造器变量被赋值。错误警告亦被解除。
   * 疑问:
     老实说,我对我在“异常描述”中的思考结果不太满意,希望有前辈告知关于这个问题,SUN的官方定义。希望看到有关于这个问题的官方说明或者有详细描述的文章。
情境二:
   根据情境一,我修改了代码。
   * ParentClass.java
     
     /**
      * 超类
      */
      public class ParentClass {

  public ParentClass(String str1, String str2) {

  }       }
     
     
   * Subclass.java
     
     /**
      * 子类,继承了ParentClass类
      */
      public class Subclass extends ParentClass {
       public Subclass(String str1, String str2) {
(5) super(getStr1(), getStr2());
}        public String getStr1() {
return null;
}

public String getStr2() {
return null;
}
      }
     
     我将“情境一”的方式二的代码在“(5)”处做出了改动,并且添加了两个获取构造器参数的方法。     * 异常:
       在“(5)”标记处编译器提示:Cannot refer to an instance method while explicitly invoking a constructor
       
       同样若这样修改代码,因为它有点换汤不换药,亦给出同样的错误提示:     
     * Subclass.java
     
       /**
        * 子类,继承了ParentClass类
        */
        public class Subclass extends ParentClass {
         public Subclass(String str1, String str2) {
  super(str1, str2);
  }   public Subclass(){
  this(getStr1(),getStr2());
  }          public String getStr1() {
  return null;
  }

  public String getStr2() {
  return null;
  }
        }
            * 异常描述:异常提示大概是说不能在调用构造器时引用一个实例方法。
       我尝试给这个错误提示一个解释,可能是说,getStr1()方法属于Subclass的实例,而构造器 Subclass(String str1, String str2) 方法是供对象创建者调用的。亦可以说,在调用 Subclass(String str1, String str2) 构造器时,Subclass 类的实例对象并未创建成功。那么,要调用属于实例对象的 getStr1()、getStr2() 自然不被允许。因此,我将 getStr1()、getStr2() 修改为静态方法,异常消失:
     * Subclass.java
       
       /**
        * 子类,继承了ParentClass类
        */
        public class Subclass extends ParentClass {
         public Subclass(String str1, String str2) {
  super(getStr1(), getStr2());
  }          //用static修饰为静态方法
          public static String getStr1() {
  return null;
  }

  public static String getStr2() {
  return null;
  }
        }
       
       貌似虽然我的这个思考结果充满了乡土气息,且并不十分健壮——所以还是如上面所说,希望能有高手提供权威的文章。但我自觉勉强能解释这个现象。但转念一想,不对,于是引出了情境三。
情境三:
     * Test.java
     
     /**
      * 为什么它可以????
      */
      public class Test {
  public Test() {
      invoke();
  }   public void invoke() {

  }
       }
     
     * 异常:
         我很想它出异常。但不可能,这是段再正常不过的代码。如果它也有异常,估计我得疯掉,我会以为我以前写类似代码并通过编译且成功运行的场景是一场梦,纠结如同《盗梦空间》。
         但经过我对情境一、二的思考过程,我此时却非常纠结:它为什么可以。既然构造器被调用,说明对象未被创建成功,为什么可以执行属于实例化对象的方法???
          说明,我的思考过程有瑕疵,或者陷入了误区或牛角尖。——我经常这样,将简单的问题复杂化:这是个成也萧何败也萧何的特点。
      * 欢迎讨论。告诉我没人盗我的梦。

解决方案 »

  1.   

    没什么奇怪的,  请看Test构造方法反编译的结果:
       0: aload_0
       1: invokespecial #1; //Method java/lang/Object."<init>":()V
       4: aload_0
       5: invokevirtual #2; //Method invoke:()V
       8: return
    可以看出, 它也是在调用父类Object的构造方法之后才调用invoke的
      

  2.   

    楼主的思考问题的方式方法很值得赞赏。
    其实原因很简单,子类在创建过程中,必须要先创建父类,这就解释了,为什么子类构造函数
    在调用父类的构造函数时,为什么必须放在第一行。你在子类构造函数如下:.............
          public Subclass(String str1, String str2) {
              super(str1, str2);
          }
    .............在super(....)处,表示正在创建其父类,此时子类还没有创建,这时候调用子类的
    getStr1,getStr2就不和逻辑了。
    至于最后一个,比较简单,构造函数中,类的对象已经创建成功,调用当前实例方法是OK的。
    其实在默认构造函数时,相当于默认在此构造函数中调用了super(),只是可以省略(相当于java做了一颗语法“糖”,简化显示调用父类的构造函数)
      

  3.   


    您的意思是在执行
     public Test() {
         invoke();    
     }
    这个构造器的时候,其实Test实例已经创建成功了,所以执行invoke是ok的。
    而,在执行
      public Subclass(String str1, String str2) {
            super(getStr1(), getStr2());
      }
    的时候,Subclass类的实例还没有创建成功,因为他还处于在调用父类构造器的阶段(Test类的构造器在invoke之前默认调用了Object的无参构造器),其实例并没有创建成功。所以,调用getStr1(), getStr2()是不符合逻辑的对吧?
    您观点的核心就是:子类构造器在执行的时候,会在第一行之前默认调用父类无参的构造器(非显式调用super())。因此执行到此之后(默认调用父类无参构造器或者显式super()调用之后)的代码时,其实子类已经被初始化成功,包括其非静态的方法也是可以调用的。
    所以解释了为什么在“情境三”中,为什么在Test类的构造器中可以调用invoke()方法。
    而,在上几例中却显示不可以——因为上几例的非静态方法调用,并非发生在父类构造器调用之后。
    对吧?
    欢迎继续讨论。
      

  4.   

    楼主你要是看了在继承条件下 jdk是如何实例化一个类的话,这些问题都很好理解.最后的一个问题,在加载子类时候,先递归调用父类带参数的构造器 ,但是你提供的两个参数是经过方法返回的:super(getStr1(), getStr2())此时类的实例还没创建,你无法通过实例来引用这两个方法,所以抛出异常至于你为什么你换成static方法就可以,很清楚。根据jdk实例化类的顺序,在执行构造器前之前,当前类及父类所有的静态方法,静态变量,静态代码块都提前执行了,所以你当然可以通过方法返回值来传递参数.实例化类的基本顺序,参考 http://jzinfo.javaeye.com/blog/620045
      

  5.   


    abstract class ParentClass {
        @SuppressWarnings("unused")
    private String str1="",str2="";
          public ParentClass(String str1, String str2) {
           this.str1=str1;
           this.str2=str2;
          }
            abstract void print();     
           }public class ChildrenClass extends ParentClass{ public ChildrenClass(String str1, String str2) {
    super(str1, str2);
    // TODO Auto-generated constructor stub
    }
       @Override
       void print(){
       
       }
    /**
     * @param args
     */
    public static void main(String[] args) {
    // TODO Auto-generated method stub }}
      

  6.   

    从情景1到情景3是oop开发很基本的东西,这个和语言无关,C++、delphi、Net也都是如此。规则1、一个类只要有父类,那么在它实例化的时候,一定是从顶级的父类开始创建。对于java来说要一直追述到Object
    祖宗(Object)-> 曾爷爷 -> 爷爷 -> 父亲 -> me
    这个思维很自然,没有祖宗,何来后代?祖宗的一些东西都没准备好,后代怎么继承去用?规则2、一个类如果显式的定义了带参构造函数,那么默认无参构造函数自动失效
    我们都知道一个类如果没有定义构造函数,那么会有一个默认的无参构造函数供你调用就是MyClass()。
    但是如果你定义了一个带参构造函数,而没有显式的定义无参构造函数,那么无参构造函数就被隐藏了。
    举个例子,把下面的代码编译一下,会报 The constructor MyClass() is undefined 错误。
    public class MyClass { public MyClass(int i){
    return;
    }

    public static void main(String[] args) {
    MyClass myClass = new MyClass();
    }
    }
    为什么会报错?呵呵,这个我也不是很明白,高人给解释一下,为什么自己定义了构造函数,默认构造函数就必须无效了。
    我自己的理解是既然定义了有参构造函数,就必须保证将参数初始化,是一种健壮性的体现(除非你自己定义无参构造函数,表示参数确实不需要显式的初始化)。规则3:类本身的装载是在实例化自身之前进行的
    类本身也是对象,只是没有初始化表示类对象之间区别的成员属性,一个没有任何成员属性的类其实就是一个【静态类】。因为你即使new了它,所有对象之间也没有区别。规则3、静态方法不属于某一个实例对象,它属于类,简单的说,一个静态方法可以不用实例化类即可通过类名直接调用。
    为什么需要静态方法?如果某个方法的执行结果和类实例对象无关或者需要对所有的实例共享,那么就有必要定义实例方法。如果理解了上面几个规则,我想这几个情景也好理解了。
    情景1:为什么在子类有参构造函数一定要先执行super(str1, str2);
    因为父类在实例化时(根据规则2)不能调用默认无参构造函数了,而你又没有去调用父类有参构造函数,编译器怎么去实例化父类?
    所以,根据1、2的规则,必须要在第一时间调用父类的一个有效地构造函数!所以,必须第一时间super(str1, str2);情景2:为什么必须使用静态方法?
    其实你自己也想明白了,关键是要理解透规则3和规则4。情景3:构造函数调用的时候本身被实例化了吗?
    你自己在6楼已经解释的很清楚了,确实是这样。情景2为什么不行是因为父类还没有实例化,所以子类阻止了在构造函数中实例化自己。
    而情景3为什么可以是因为这些事已经被默认构造函数给做了(因为你没有阻止它,呵呵。)顺便问一句,盗梦空间好看吗?
      

  7.   

    一般来说,如果要写带参数的构造方法,需要把不带参的构造方法也写上,
    还有构造方法的返回值必须是本身即this.
      

  8.   

    13楼的关于为什么JAVA要被设计为“定义了带参的构造器,无参的默认构造器无效”的解释很富有逻辑:
      
           我自己的理解是既然定义了有参构造函数,就必须保证将参数初始化,是一种健壮性的体现(除非你自己定义无参构造函数,表示参数确实不需要显式的初始化)。
      
      
      

  9.   

    CSDN的非法词组检测是不是敏感得过头啦?我又被提示有非法词组不能回复。我晕,语法这么严格?不如出个debug??我靠!
      

  10.   

    7楼推荐了一篇文章:http://jzinfo.javaeye.com/blog/620045
    文章讨论了一个类被实例化的详细过程。
    其第二例讨论了多重继承状态下的子类在实例化时,的执行过程。示例代码很有意思://此类不参加继承讨论,但是会被下面三个有继承关系的类反复调用其构造器。
    class DogProfile {
        DogProfile(String male, Integer age) {
            ......
    System.out.println("DogProfile构造方法");
        }
    }//狗的祖宗类,接下来
    class Dog { 
        private static DogProfile profile = new DogProfile("公", 3);    static {
    System.out.println("Dog的静态代码区域");
        }
     
        Dog(String name) {
    System.out.println("Dog的名字是:" + name);
        }
    }//聪明的我猜这是条京巴狗
    class JingbaDog extends Dog {
         private static DogProfile profile = new DogProfile("母", 2);     JingbaDog(String name) {
    super(name);
    System.out.println("JingbaDog的名字是:" + name);
         }
    }
    class DogExamp extends JingbaDog { 
          private static DogProfile profile = new DogProfile("母", 1);      DogExamp(String name) {
       super(name);
       System.out.println("DogExamp的名字是:" + name);
          }      //入口在这
          public static void main(String[] args) {
        new DogExamp("不知名");
          }
    }
      

  11.   

    问:此程序的执行过程分析:我想可能普遍会有两种解释——
       一. 严格贯彻“每个类是在被访问到时才被加载进JVM,此时会执行其静态动作”的指导思想。
          1. 从入口 main 方法开始,由于 DogExamp 被访问,因此它被加载。然后执行其静态代码:
                  private static DogProfile profile = new DogProfile("母", 1);
              因此,调用到 DogProfile 对应的构造器:
                  System.out.println("DogProfile构造方法");
              打印:“DogProfile构造方法”
         
          2. 再按楼上兄弟们的说法,子类实例化时,先实例化其父类。何况它还显示得在构造器第一行调用了super(name)----
               + DogProfile 的父类 JingbaDog 被访问到,于是初始化它的静态代码:
                    JingbaDog的第一行:new DogProfile("母", 2);
                 再次执行 DogProfile 的构造器:System.out.println("DogProfile构造方法");
               + 执行 JingbaDog 的构造器,同样先实例化父类——它也显式调用了super(name);
               + 祖宗 Dog 被访问到,于是JVM加载它。同样先执行其静态动作:
                     new DogProfile("公", 3);  --> System.out.println("DogProfile构造方法");
                     再执行--
                     static {
          System.out.println("Dog的静态代码区域");
             }
                     再执行--
                     Dog(String name) {
                           System.out.println("Dog的名字是:" + name);
                     }
                 + 再返回到 JingbaDog 构造器中第二行代码:
                     System.out.println("JingbaDog的名字是:" + name);
                 + 再返回到 DogExamp 构造器第二行代码:
                     System.out.println("DogExamp的名字是:" + name);
          3. 执行完毕,DogExamp 被初始化成功,控制台打印一行行语句。
      

  12.   

    经过我的测试CSDN不能发-- 代 码 开 始 --这四个字组合的词组(去掉中间的空格)。
    测试得很辛苦,运用了先进的异常调试方法。
    我想,这算是“特别的地方了吧”
      

  13.   

     二. 当实例化一个子类时,无论如何先初始化其“祖宗类”,再一层层初始化其父类。最后子类被初始化。
            1. 那么应该是 Dog 首先被初始化,即它的静态代 码 开 始 执行(加载其静态方法,为其静态变量赋值,执行静态代码块),于是首先 Dog 类的第一行被执行到:
                     private static DogProfile profile = new DogProfile("公", 3);
                进入 DogProfile 构造器,执行 ---> System.out.println("DogProfile构造方法");
                (这一步可能又有异议,是否是先执行到 new DogExamp() 时,才会触发 Dog 被加载和初始化呢?)
            2. 执行 Dog 类的静态代码块:(我在想是不是然后跑到Object里去了,应该)
                    static {
          System.out.println("Dog的静态代码区域");
            }
            3. 初始化 JingbaDog ,执行其第一行代码:
                    private static DogProfile profile = new DogProfile("母", 2);
                进入 DogProfile 的构造函数,打印:System.out.println("DogProfile构造方法");
            4. 初始化 DogExamp, 执行其第一行代码:
                     private static DogProfile profile = new DogProfile("母", 2);
                进入 DogProfile 的构造函数,打印:System.out.println("DogProfile构造方法");
            5. 再执行 DogExamp 的 main 方法,执行其“new DogExamp("不知名")”代码。
            6. 执行 DogExamp 构造器中的“super(name)”,进入到 JingbaDog 的构造器。
            7. 执行 JingbaDog 的构造器,其“super(name)”代码,使流程进入到“Dog”的构造方法。
            8. 打印:System.out.println("Dog的名字是:" + name);
            9. 回到 JingbaDog 的构造器继续执行--
                  System.out.println("JingbaDog的名字是:" + name);
            10. 回到 DogExamp 的构造器继续执行:
                  System.out.println("DogExamp的名字是:" + name);
            11. new DogExamp("不知名")的全部流程执行完毕,main 也结束了。----------
    事实证明第二种猜想是正确的。其实我最开始的时候以为是第一种的,第二种是我看到运行结果后才想到的。我想这就是我的误区所在。大家可以在 Dog 类的“private static DogProfile profile = new DogProfile("公", 3);”打一个断点,再在 DogExamp 类的 mail 方法内的 “new DogExamp("不知名");” 代码处打上断点。
    再以debug方式运行程序,可以很清晰的看到程序的处理流程。结论就是:当访问到一个类时,总是先从其最顶层的父类开始,向下初始化。
    本例中,DogExamp的main被虚拟机访问到。因此,触发了从Dog类开始的向下初始化。
    并非是执行到“new DogExamp("不知名")”才开始的。欢迎指正这个结论。结论不完整的是我的debug调试结果显示 Object 没有参加这个向下初始化的流程。是否它是在虚拟机启动的时候便完成初始化的?7 楼 贴出的文章网址是:http://jzinfo.javaeye.com/blog/620045
    debug跑完这个程序,再看 13楼  NewMoons 的精辟结论,我相信很多人都会有收获。
      

  14.   

    如果楼主系统地学习过 java 基础,就不会感到搞了那么久才偶然发现这些规律
      

  15.   

    百度上看到的一个很好的:
    没有父类怎么构造子类啊,构造子类之前是必须要先构造父类的,一般都是隐式调用父类的无参构造器的,所以:1.如果定义了有参构造器,而又没有无参构造器的话,JVM是不会自己构造无参构造器的,因为虚拟机认为你自己可以定义构造器(java虚拟机说:自己能做为什么还要我帮你定义无参构造器呢)2.如果没有定义了有参构造器,就可以不用自己定义无参构造器(java虚拟机说:你不会定义构造器啊,那我帮你造个吧) 
      

  16.   

    确实是这样,有时候自认为基础过了。但是会慢慢发现,其实还是有很多东西值得推敲。而这个推敲的过程,可以帮助你跳出语言的桎梏,看到更多本质上的东西。
    我推崇的学习方式是:在学习一门语言的时候,始终将自己当成这门语言的设计者;在学习一个框架的时候,始终将自己当成这个框架的作者,并同时持质疑者和欣赏者的态度。
    尽量思考一些靠近本质的东西。从我们的行业习惯来讲,这其实是为了“模型重用”,越是本质的东西,其普遍适用性越强。我们慢慢会发现,原来在编码的过程中我们还可以控制更多的东西。
    例如35楼的NewMoons的思考我就非常欣赏,他问:为什么自己定义了构造函数,默认构造函数就必须无效了?保持有效不行吗?
    很多人可能觉得妈的,这不是脑残么?!这还什么为什么不为什么的??人家就是这么设计的呗!
    可是,我们换个角度,将自己想想成现在你在设计一门新的语言,你也要这么干么?为什么?
      

  17.   

    3楼的前辈一句:相当于java做了一颗语法“糖”,简化显示调用父类的构造函数。
       ---“糖”一字很精辟。
    越高级的语言,越是给了你很多的“糖”。你发现真是甜死了,爽死了,以前要花很多时间才能完成的功能、要花很多时间关注的一些细节,它都直接提供给你用了,真好。
    举例说,JAVA基于C++来说,简直遍地都是“糖”。意味着,其底层为你屏蔽(透明化)了狠多东西——同时也意味着你能控制的东西愈来愈少。
    这也是为什么有了JAVA这么“甜”的东西,还是有很多项目需要选择C++甚至C来完成。
      

  18.   

    LZ 没看Core java吧,里面说得好清楚
      

  19.   

    楼主,父类和子类的实例化顺序没有弄清楚,父类是在子类前先实例,所以你在  public class Subclass extends ParentClass {
                public Subclass(String str1, String str2) {
              super(getStr1(), getStr2());
          }这个方法是错误的,我现在父类都还没有实例,你就调用子类的 实例方法,同时 静态方法是类的行为,而不是实例对象,所以你在 public class Subclass extends ParentClass {
                public Subclass(String str1, String str2) {
              super(getStr1(), getStr2());
          }          //用static修饰为静态方法
              public static String getStr1() {
              return null;
          }
        
          public static String getStr2() {
              return null;
          }
    是正确的
      

  20.   

    我只想知道你到底和java合作了多久?是怎么合作的?
      

  21.   

    在子类实例化的时候,父类会默认被实例化的。所以需要一个默认的无参构造器,一个类在没写构造器的时候有一个默认的无参构造器,但你写了一个有参的构造器后,就需要写上一个无参的构造器了。
    给个例子:// 父类
    public class Parent {
    public Parent(){
    System.out.println("Parent instantiation...");
    }
    }
    // 子类
    public class Child extends Parent{
    public Child(){
    System.out.println("Child instantiation...");
    }
    }
    // 测试类
    public class Test2 {
    public static void main(String[] args) {
    Child child = new Child();
    }
    }
      

  22.   

    一直都是这么理解生成对象的过程的:
    new 时,如果是第一次new并且该类没有被加载过,就把父子类同时加载到内存,然后1、父子static属性缺省值初始化,父子static方法激活可用,2、按照顺序依次显式初始化父static属性,执行父static块,3、显式初始化子static属性,执行子static块。4、缺省初始化所有的父子非static属性,非static方法激活可用,5、找到对应的子类构造方法开始执行,第一句调用父类构造,一直追溯到Object构造,之后下来显式初始化父类非static属性 6、执行父类构造 7、父类构造执行完,显式初始化子类非static属性,执行子类构造的第二句。最后对象生成完毕。但是在显式调用super时,不能调用子类非static方法,说明上面的理解是错误的吗?难道子类的非static方法是在父类构造执行完之后,才激活可用的?
    ---这么理解的话,那子类的非static属性是在什么时候缺省初始化呢?父类构造之后吗?
      

  23.   

    有些同学都说得不错,学习了!但关于那个:构造器里调用自身的方法是ok的解释:public class Test {
          public Test() {
              invoke();    
          }      public void invoke() {
            
          }
           }
    有几位同学都默认是:因为子类在隐式地调用完父类Object构造器后就已经完成对象初始化了这个说法我觉得非常有瑕疵,应该说构造方法里的所有代码都执行完才能叫完成对象的初始化(对象实例创建好),如果说只执行下父类的构造方法就叫已经创建好对象,那我们再化简下:到Object无论相隔几代只要Object的构造器被调用了,那子类对象其实就可以叫创建完毕了,把其他代码放到构造器里去执行也变得好像不是创建一个对象必须的,那干脆不放里面了放其他方法去也罢,跟对象初始化无关啊,你们说这很可笑吧。至于如何理解在类在自己的构造器中调用自己的方法呢?其实他就相当于把invoke方法里的代码拷贝进去构造器里执行而已,这样就有一个很好的初步理解了,因为我们不经常在构造器里写几行初始化代码嘛这里有人要问:
    java不是需要对象实例才能调用实例方法吗?(非要理解成在构造器里调用实例方法也成)为什么要做这种限制我想大家也许早就听了N遍了:(简洁地)需要先在内存里“有东西”(静态可以直接用是因为早就有了)
    在这里也一样,关键是在内存中“要用到的东西”是否存在(主要是方法里可能用到的变量)
    方法代码其实早就装载好了就等类的成员完成初始化(父类自然更早完成初始化),实际上就是等方法里会用到的“实例变量”初始化完,那么方法就可以进行使用了,那我们知道会执行到构造器那表明成员的初始化完了,所以在构造器里调用自己的非静态方法是不会出现非法/不安全的操作的,因为方法里会用到的“变量”(当然可能是父类变量)早就准备好了,所以java自然也会让你这么做——在构造器里调用自己的非静态方法。
      

  24.   

    個人認為在用戶顯示調用父類或系統自動調用父類構造方法后子類對象就已經創建成功了,因為在子類構造方法的第一句(用戶看到的是第一句,實際上正好前面幾位所言,第一句永遠都是父類的構造方法)就能訪問到this對象了,因此子類對象已經創建成功,並且父類和子類的成員變量也已經初始化好了,并被賦予的系統默認值或用戶值如果錯誤,還望大家指正,共同學習
      

  25.   

    我总结了一下实例化的顺序应该是:
      1. 确定执行的类是谁?比如,我在33楼用到的例子,入口函数在 DogExamp 类。  2. 那么,就开始从 DogExamp 的最上层的父类开始初始化静态代码 --> 为静态变量初始化值、执行静态代码块. 这个过程一层层由父类到子类,一直到“执行类”为止。我是说:假如 DogExamp 类以下还有子类,但它的子类不会进入这个过程。可以说:静态代码的执行过程是由顶层父类到“执行类”(不知道怎么描述这个概念)的由上往下的执行过程。 -- 这个时机应该仅次于类加载的时机。
      
       3. 在父类以及本身的静态代码执行完毕之后。然后会在子类被第一次访问到的时候(包括 New 行为),触发其直接父类的构造器执行。其父类再会触发它的直接父类的构造器执行。可以说,触发过程是由下往上的。33 楼的 debug 试验证明了这一点。
         另外:与触发过程的由下往上相反,总是在父类的构造器执行完毕之后,子类的构造器才会被执行。亦是说,一个类的实例化过程为:总是在父类被实例化完毕之后,子类才会被实例化。
       
       4. 我在 29、30、33 楼用到的例子和分析方法有助于还不清楚这个过程的朋友理解它。-- 另外,debug不只是调试工具,也能流程查看器。   5. 我觉得这个解释是靠谱的。这个可以解释我在1楼的现象。