理解实例化

http://www.chineselinuxuniversity.net/articles/43910.shtml

对象实例何时被创建,这个问题也许你用一句话就能回答完了。但是它的潜在陷阱却常常被人忽视,这个问题也许并不像你想的那么简单,不信请你耐心看下去。
    我前几天问一个同学,是不是在调用构造函数后,对象才被实例化?他不假思索的回答说是。
    请看下面代码:
    Java代码
    Date date=new Date();
    em.out.println(date.getTime());
    新手在刚接触构造函数这个概念的时候。他们常常得出这样的结论:对象实例是在调用构造函数后创建的。因为调用构造函数后,调用引用(date)的实例方法便不会报NullPointerException的错误了。
    二、经验者的观点
    然而,稍稍有经验的Java程序员便会发现上面的解释并不正确。这点从构造函数中我们可以调用this关键字可以看出。
    请看下面代码:
    Java代码
   

public class Test{
    public Test()
    {
         this.DoSomething();
    }
    private void DoSomething()
    {
        System.out.println("do init");
    }
}   

 

 

 


    这段代码中我们在构造函数中已经可以操作对象实例。这也就证明了构造函数其实只是用于初始化,早在进入构造函数之前。对象实例便已经被创建了。
    三、父类构造函数
    当创建一个有父类的子类的时候。对象的实例又是何时被创建的呢?我们也许接触过下面经典的代码:
    Java代码
   
 public class BaseClass{
    public BaseClass()
    {
        System.out.println("create base");
    }
}
public class SubClass{
    public SubClass()
    {
        System.out.println("create sub");
    }
    public static void main(String[] args)
    {
        new SubClass();
    }
}
 
    结果是先输出create base,后输出create sub。这个结果看起来和现实世界完全一致,先有老爸,再有儿子。因此我相信有很多程序员跟我一样会认为new SubClass()的过程是:实例化BaseClass->调用BaseClass构造函数初始化->实例化SubClass->调用SubClass构造函数初始化。然而非常不幸的是,这是个错误的观点。
    四、奇怪的代码
    以下代码是为了驳斥上面提到的错误观点。但是这种代码其实在工作中甚少出现。
    Java代码
    public class BaseClass
    {
    public BaseClass()
    {
    System.out.println("create base");
    init();
    }
    protected void init()
    {
    System.out.println("do init");
    }
    }
    //
    public class SubClass
    {
    public SubClass()
    {
    System.out.println("create sub");
    }
    @Override
    protected void init()
    {
    assert this!=null;
    System.out.println("now the working class is:"+this.getClass().getSimpleName());
    System.out.println("in SubClass");
    }
    public static void main(String[] args)
    {
    new SubClass();
    }
    }

 


    这段代码运行的结果是先调用父类的构造函数,再调用子类的init()方法,再调用子类的构造函数。
    这是一段奇妙的代码,子类的构造函数居然不是子类第一个被执行的方法。我们早已习惯于通过super方便的调用父类的方法,但是好像从没这样尝试从父类调用子类的方法。
    再次声明,这只是个示例。是为了与您一起探讨对象实例化的秘密。通过这个示例,我们再次印证了开头的观点:早在构造函数被调用之前,实例便已被创造。若该对象有父类,则早在父类的构造函数被调用之前,实例也已被创造。这让java显得有些不面向对象,原来老子儿子其实是一块儿出生的。
    五、奇怪但危险的代码
    本篇是对上篇奇怪代码的延续。但是这段代码更加具有疑惑性,理解不当将会让你出现致命失误。
    请看下面代码:
    Java代码
    public class BaseClass {
    public BaseClass()
    {
    System.out.println("create base");
    init();
    }
    protected void init() {
    System.out.println("in base init");
    }
    }
    public class SubClass extends BaseClass{
    int i=1024;
    String s="13 leaf";
    public SubClass()
    {
    System.out.println("create sub");
    init();
    }
    @Override
    protected void init() {
    assert this!=null;
    System.out.println("now the working class is:"+this.getClass().getSimpleName());
    System.out.println("in SubClass");
    /great line/
    System.out.println(i);
    System.out.println(s);
    }
    public static void main(String[] args) {
    new SubClass();
    //oh!my god!!
    }
    }
    这段代码相比上一篇,只是在子类中添加了一些成员变量。而我们的目标正是集中在讨论成员变量初始化的问题上。
    这段代码的执行顺序是:父类、子类实例化->调用父类构造函数->调用子类init()方法->调用子类构造函数->调用子类init()方法。最终的输出结果向我们揭示了成员变量初始化的秘密。
    当父类构造函数调用子类的init()方法的时候。子类的成员变量统统是空的,这个空是指的低级初始化。(值类型为0,布尔类型为false,引用类型为null)。而当子类构造函数调用init()方法的时候,成员变量才真正被初始化。这是一个危险的讯息,那就是使用父类构造函数调用子类时存在成员变量未初始化的风险。
    我们的讨论也到此为止了。再次回顾,总结一下实例何时被创建这个问题。我得出了以下结论:
    本文到此便结束了。鉴于本 人才疏学浅,若是专业术语有错误,或是哪里讲的不对,也欢迎各位高手拍砖。
    附上第五篇中SubClass的部分字节码,方便大家深入理解:
    Java代码
    public SubClass();
    aload_0 [this] //aload_0是啥?
    invokespecial ques.BaseClass() [26] //调用父类构造函数
    aload_0 [this]
    sipush 1024 //初始化i成员变量
    putfield ques.SubClass.i : int [28]
    aload_0 [this]
    ldc "13 leaf"> [30] //初始化s成员变量
    putfield ques.SubClass.s : java.lang.String [32]
    getstatic java.lang.System.out : java.io.PrintStream [34]
    ldc "create sub"> [40]
    invokevirtual java.io.PrintStream.println(java.lang.String) : void [42]
    aload_0 [this]
    invokevirtual ques.SubClass.init() : void [48] //调用init
    return
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值