对于长期使用C++的人来说,这样的语义确实叫人一下子难以接受。/*
 * Main.java
 *
 * Created on 2006年8月17日, 下午8:28
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */package javaapplication2;/**
 *
 * @author Zenny Chen
 */class Parent
{
    protected int t;
    
    public Parent()
    {
        System.out.println("Creating Parent...");
        create();
    }
    
    public void create()
    {
        System.out.println("Parent created!");
        
        t = 1;
    }
}class Child extends Parent
{
    private int c;
    
    public void create()
    {
        c = 1;
        System.out.println("Child created!");
    }
    
    public Child()
    {
        System.out.println("Creating Child...");
        create();
    }
    
    public int getValue()
    {
        return c + t;
    }
}public class Main {
    
    /** Creates a new instance of Main */
    public Main() {
    }
    
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        // TODO code application logic here
        
        Child ch = new Child();
        
        System.out.println("The result is: "+ch.getValue());
    }
}从语义学和工程学角度,在Child对象被创建时首先要创建其父类域,然后可以根据父类属性
来获得创建自身资源的需要。然而Java在处理方法重写这个语义时,执行的是“一刀切”,
父类域尚未被创建完就开始调用其子类所重写的方法来。这样很可能造成子类的资源也无法
被初始化(若子类构造方法不去构建)。
从这点上,C++做得就比较好。创建一个对象就像造房子,一层一层建造。C++中虚函数的多态性
不会表现在构造函数和析构函数中,而且构造函数和析构函数也不会受到const对象的限制。
尽管在语义的实现上确实要比Java复杂一点,但符合语义学和工程学。

解决方案 »

  1.   

    这个的确是,JVM规定是这样,不过这点只是JVM内部的机制,不影响我们写程序的。
      

  2.   

    C++里面的操作符重载,临时对象,模板等语法,使得大量代码非常简洁,并且美观。
    我最喜欢C++ Templates里面那个例子了。
    不看不知道,那样一封装,模板展开了循环,速度超过了机器码;而且重载了运算符,计算几个矩阵的值,看起来就像数学公式那样漂亮,而且类型安全。
      

  3.   

    这个的确是,JVM规定是这样,不过这点只是JVM内部的机制,不影响我们写程序的。
      

  4.   

    这确实是个问题,不过我认为在初始构造中保持逻辑的简单是很有必要的,即使是在C++中,过分复杂的初始化逻辑还是会造成很多麻烦,所以初始化还是尽可能的保持简单性原则比让JAVA或C++来维护逻辑正确性优先级更高一些@.@||~
      

  5.   

    不要这样说好不好~
    JAVA就是一种实用的语言,是有很多地方为了简化而舍弃了严谨性。
    换来的好处是非常明显的:JAVA普及不过就楼主给的例子,我认为这没什么,道理很简单啊:
    你既然知道子类没有构建完,那你为什么还要调用重载方法呢?请给我一个理由!语言是死的,人是活的!
    我就有一个编程原则:在类的加载代码、对象的初始化代码、构造方法中,不做与初始化成员的动作。如果你也能遵守,就不会指责JAVA的不是了。
      

  6.   

    你可能辩解说:
    我定义Parent的时候没有想到Child会重载create();但是作为Child的作者,你应该知道在这种情况下,你很明显不能用继承!
    组合应该是更好的选择!==========
      

  7.   

    抓住别人的小问题不放,有什么用.
    问一下自己为什么要使用JAVA
      

  8.   

    请教 hbwhwang:
    > 我就有一个编程原则:在类的加载代码、对象的初始化代码、构造方法中,不做与初始化成员无关的动作。楼主的这段程序,create() 从内容上看,也算是“初始化成员”的动作呀?那可不可以这样理解:这种“初始化成员”的操作,比如 create(),不应该用 public ?
    反正我把 Parent 里的 create() 改成 private,倒是能达到预期的效果。
      

  9.   

    To hbwhwang(catmiw的ID已经停用,现在用这个) :当然,在Java中可以进行做稍许弥补:
    在子类的create()方法中在开头添加:super.create();不过这样看上去有点别扭。【
    语言是死的,人是活的!
    我就有一个编程原则:在类的加载代码、对象的初始化代码、构造方法中,不做与初始化成员的动作。如果你也能遵守,就不会指责JAVA的不是了。

    我不反对你这么说,不过如果对于编程语言本身能够提供更科学的语义,对于
    开发人员来说也是种福气。当然,Java在很多方面确实比C++更“动态”,也有自己的优势,
    语言的使用者当然要遵守它的规则,不是自己说的算的。这里列出这个问题也想提醒从C++到Java或从Java到C++的使用者,引起注意。
      

  10.   

    顺便牵出另外一个问题:如果我在 Child 里重载了 Parent 里的 create(),那么,在 Parent 的构造方法里,难道没有什么办法*显式*调用自己的 create() 而不是 Child 里的 create() 吗?我用 Parent.this.create(),可发现实际执行的还是 Child 里的 create(),难道真的没办法了吗?
      

  11.   

    【接上帖】也就是说,父类里的一个 public 方法,一旦被子类重载,那么,只有子类对象可以选择使用那个版本,而父类自己是没有这个选择权的。是这样吗?
      

  12.   

    maquan('ma:kju):“如果我在 Child 里重载了 Parent 里的 create(),那么,在 Parent 的构造方法里,难道没有什么办法*显式*调用自己的 create() 而不是 Child 里的 create() 吗?我用 Parent.this.create(),可发现实际执行的还是 Child 里的 create(),难道真的没办法了吗?”
    ===========你真会问问题,专往JAVA的死穴上点。
    你在下次JAVA规范制定的时候,提出一个“if_you_invoked_my_children's_method__i_will_kill_you”关键字,这样就应该OK了。
      

  13.   

    重写后无情的穿透性.
    我觉得也可以理解,既是,想要使用那种类型的方法,取决于在内存创建时创建那种类型的对象.而不是用哪种引用来使用对象.
    不管怎样.LZ这种做法本身是不高明的.上层类在实现的时候没有把自己的方法保护起来,使得子类有机会重载,并且,子类在没有调查父类便重写父类的方法.子类的重载初期使用SUPER.CREATE();或者父类把仅仅初始化自己字段的方法PRIVATE掉.就不会出现LZ的问题.
      

  14.   

    java 是一门独立的语言,就好比不要用英文的语法来要求中文。
      

  15.   

    老问题了,这是初学者常犯的错误,在Effective Java一书中多次提到:
    对于一个用来被继承的类,不要在其构造函数中调用可以被之类覆写的方法
      

  16.   

    本来就是这么会是呀,子类覆盖了父类的方法呀。如果你想父类的方法不被覆盖,加final修饰。