`
sd6733531
  • 浏览: 66571 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

对象实例是何时被创建的?

 
阅读更多

 

对象实例何时被创建,这个问题也许你用一句话就能回答完了。但是它的潜在陷阱却常常被人忽视,这个问题也许并不像你想的那么简单,不信请你耐心看下去。

我前几天问一个同学,是不是在调用构造函数后,对象才被实例化?他不假思索的回答说是。

  请看下面代码:

           Date date=new Date();

       System.out.println(date.getTime());
 

新手在刚接触构造函数这个概念的时候。他们常常得出这样的结论:对象实例是在调用构造函数后创建的。因为调用构造函数后,调用引用(date)的实例方法便不会报NullPointerException的错误了。

二、经验者的观点

    然而,稍稍有经验的Java程序员便会发现上面的解释并不正确。这点从构造函数中我们可以调用this关键字可以看出。

请看下面代码:

public class Test

{

  public Test()

  {

      this.DoSomething();

  }

  

  private void DoSomething()

  {

      System.out.println("do init");

  }

}
 

    这段代码中我们在构造函数中已经可以操作对象实例。这也就证明了构造函数其实只是用于初始化,早在进入构造函数之前。对象实例便已经被创建了。

三、父类构造函数

       当创建一个有父类的子类的时候。对象的实例又是何时被创建的呢?我们也许接触过下面经典的代码:

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构造函数初始化。然而非常不幸的是,这是个错误的观点。

四、奇怪的代码

       以下代码是为了驳斥上面提到的错误观点。但是这种代码其实在工作中甚少出现。

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显得有些不面向对象,原来老子儿子其实是一块儿出生的。

五、奇怪但危险的代码

       本篇是对上篇奇怪代码的延续。但是这段代码更加具有疑惑性,理解不当将会让你出现致命失误。

       请看下面代码:

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的部分字节码,方便大家深入理解:

public SubClass();

     0  aload_0 [this] //aload_0是啥?

     1  invokespecial ques.BaseClass() [26] //调用父类构造函数

     4  aload_0 [this]

     5  sipush 1024 //初始化i成员变量

     8  putfield ques.SubClass.i : int [28]

    11  aload_0 [this]

    12  ldc <String "13 leaf"> [30] //初始化s成员变量

    14  putfield ques.SubClass.s : java.lang.String [32]

    17  getstatic java.lang.System.out : java.io.PrintStream [34]

    20  ldc <String "create sub"> [40]

    22  invokevirtual java.io.PrintStream.println(java.lang.String) : void [42]

    25  aload_0 [this]

    26  invokevirtual ques.SubClass.init() : void [48] //调用init

    29  return
分享到:
评论

相关推荐

    js 面向对象实例

    在这个例子中,`Person`是构造函数,`this`关键字用来引用新创建的对象实例。你可以通过`new`关键字来创建并初始化一个新对象: ```javascript let person1 = new Person('Alice', 25); ``` 原型是JavaScript中的...

    详解C# 利用反射根据类名创建类的实例对象

    “反射”其实就是利用程序集的元数据信息。 反射可以有很多方法,编写程序时请先导入 System.Reflection 命名... // 创建类的实例 2、若要反射当前项目中的类(即当前项目已经引用它了)可以为: Assembly assembly

    未将对象引用设置到对象的实例问题原因

    这一异常通常意味着试图访问一个未被正确实例化的对象。下面将详细探讨这种异常的常见原因,并提供相应的解决策略。 #### ViewState 对象为Null **问题描述:** 当试图访问某个页面的ViewState(一种用于保存页面...

    java类与对象实例

    "java类与对象实例" 在 Java 中,类和对象是两个紧密相连的概念。类是一种蓝图,用于创建对象,而对象是类的一个实例。类描述了对象的基本结构,包括属性、方法和事件等。类设计的关键概念包括封装、继承和多态。 ...

    java实例化对象的过程

    在Java编程语言中,实例化对象是创建类的实例,使其具有特定状态和行为的过程。这个过程涉及到几个关键步骤,让我们深入探讨一下。 首先,我们需要一个类,它是创建对象的蓝图。在Java中,我们使用`class`关键字来...

    u8不能创建对象实例-重新注册批处理脚本

    解决打开U8模块后提示“不能创建对象实例”,重新注册dll的批处理脚本。

    java 基础 类对象创建实例化过程 实例解析

    * 执行启动类的main函数 -&gt; 创建对象的继承树从高到底的类层次中的静态块(如果已经被执行过,则不会重复执行) -&gt; * 继承树中的从最高层开始的实例语句块{}、然后对应的构造函数、以及构造函数中调用的方法 * ...

    Javascript创建自定义对象 创建Object实例添加属性和方法

    如下所示: 代码如下: var person...上面的例子创建了一个名为person的对象,并为它添加了三个属性(name、age和job)和一个方法(sayName())。其中,sayName()方法用于显示this.name()的值。早期的JavaScript开发人员

    PHP把实例化的class对象永久保存

    PHP把实例化的class对象存入session(session的值写入memcache),不必每次都创建类的实例 听起来有点绕口,其实就是: 1.不管你网站有多大,且有多少个class,用此方法之后每个类只需要实例化一次(除非你重启了服务器),听...

    asp输出json对象实例

    标题"asp输出json对象实例"指的是在ASP中创建和输出一个JSON对象,而不是简单的JSON字符串。通常,我们需要先将ASP中的数据结构(如数组或对象)转化为JSON格式,然后通过HTTP响应输出到客户端。这个过程中可能会...

    WebGL实例化.zip

    然而,实例化允许开发者通过共享相同的几何数据,仅改变每个实例的属性(如位置、旋转、缩放)来创建多个对象,从而显著提升渲染效率。 在“实例化.html”文件中,很可能展示了如何设置WebGL上下文,定义一个基本的...

    C#面向对象编程_对象(类的实例)

    在创建类的实例后,可以通过对象直接访问公共字段并对其进行赋值。对于私有字段,则通常需要通过公开的属性或方法来间接访问和修改。 ```csharp mCT.m_Field = "这是一个测试字段"; // 访问字段 ``` 在提供的`...

    Java实例化一个抽象类对象的方法教程

    由于抽象类自身不能被实例化,这意味着你不能使用`new`关键字直接创建一个抽象类的对象。然而,尽管不能直接实例化,抽象类在特定情况下依然可以间接地创建对象。以下将详细介绍如何在Java中处理抽象类实例化的问题...

    利用Type动态创建类实例(C#反射)可以演变抽象工厂

    例如,`typeof()`关键字用于获取类型对象,`Activator.CreateInstance()`方法则用于根据Type对象动态创建类的实例。这种能力使得代码更具灵活性和可扩展性,特别是在处理未知类型或需要动态行为的场景下。 2. 抽象...

    Dreamweaver 8中文版实例教程 第3章 创建网页基本对象

    Dreamweaver 8中文版实例教程 第3章 创建网页基本对象

    类,对象实例

    在编程领域,特别是使用C#这种面向对象的语言时,"类"和"对象实例"是核心概念。类是创建对象的蓝图,它定义了对象的属性(数据成员)和行为(成员函数)。对象实例则是根据类创建的具体实体,每个实例都有自己的属性...

    第四章示例代码__对象的实例化方式

    首先,让我们来看看在Python中的对象实例化。Python是一种动态类型的编程语言,实例化一个对象非常直观。例如,如果我们有一个名为`Person`的类,我们可以通过以下方式创建它的实例: ```python class Person: def...

    构造函数 原型对象 实例、图解

    从图中可以看出,构造函数创建了原型对象,而原型对象则被实例继承。实例继承了原型对象的属性和行为,并可以拥有自己的属性和方法。 对象的 __proto__ 属性 在 JavaScript 中,每个对象都有一个 `__proto__` 属性...

    Jsp的隐式对象实例教程文档

    在JSP中,有一些预先定义好的对象,被称为“隐式对象”,这些对象可以直接在JSP页面中使用,无需显式创建。这些隐式对象极大地简化了开发过程,提高了效率。本教程将详细介绍JSP的九个主要隐式对象及其用途。 1. **...

    C#中动态声明与使用对象数组实例

    这里,`new`关键字用于创建对象实例,`MyClass`是类名,`obj`是分配给该实例的变量名。 **数据保护** 在处理数组时,数据保护至关重要。C#提供了一些内置机制来确保数据安全。例如,通过使用访问修饰符(public, ...

Global site tag (gtag.js) - Google Analytics