您可能感兴趣的话题:

Java

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

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

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

请看下面代码:

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

java中何时不能实例_Java对象实例是何时被创建相关推荐

  1. java中的string函数_java中string.trim()函数的作用实例及源码

    trim()的作用:去掉字符串首尾的空格. public static void main(String arg[]){ String a=" hello world "; Str ...

  2. android socket 发送byte_如何正确地创建和销毁网络通讯程序中的Socket类的对象实例...

    软件项目实训及课程设计指导--如何正确地创建和销毁软件应用系统中网络通讯中的Socket类的对象实例 1.基于TCP/IP协议的Socket通信相关的基础知识 (1)TCP/IP(Transmissi ...

  3. 将java中数组转换为ArrayList的方法实例(包括ArrayList转数组)

    方法一:使用Arrays.asList()方法 1 2 String[] asset = {"equity", "stocks", "gold&quo ...

  4. immutable java_Java中的mutable和immutable对象实例讲解

    1.mutable(可变)和immutable(不可变)类型的区别 可变类型的对象:提供了可以改变其内部数据值的操作,其内部的值可以被重新更改. 不可变数据类型:其内部的操作不会改变内部的值,一旦试图 ...

  5. java创建一个人函数类_Java对象和类–学习如何创建和实现

    对象和类被认为是任何基于OOPs的语言的精华.由于Java是最常用的面向对象编程语言之一,因此非常有必要牢记这些概念.因此,我为您带来这篇文章,在这里我将给您一个完整的Java对 对象和类被认为是任何 ...

  6. Java 中,类、类对象、泛型之间的转换

    Java 中,类.类对象.泛型之间的转换 R 为非泛型 获得类 通过类型名来获得类 通过对象来获得类 通过类名字符串来获得类 通过类来获得类名字符串 通过类来获得对象 使用 R 的无参数构造器来创建对 ...

  7. Java中,一切皆是对象,为何数据类型中还分为:基本类型和对象?

    Java中,一切皆是对象!为何数据类型中还分为:基本类型和对象?按理不应该只有一个类型-对象吗? 1.首先,Java中一切皆是对象!这句话没错,因为八种基本类型都有对应的包装类(int的包装类是Int ...

  8. java中使用Semaphore构建阻塞对象池

    java中使用Semaphore构建阻塞对象池 Semaphore是java 5中引入的概念,叫做计数信号量.主要用来控制同时访问某个特定资源的访问数量或者执行某个操作的数量. Semaphore中定 ...

  9. Java中的垃圾回收与对象生命周期

    转载自   Java中的垃圾回收与对象生命周期 1. 垃圾回收 垃圾回收是Java程序设计中内存管理的核心概念,JVM的内存管理机制被称为垃圾回收机制. 一个对象创建后被放置在JVM的堆内存中,当永远 ...

最新文章

  1. 【android-tips】android程序执行adb shell命令(实例源码)
  2. php常用快捷键,phpstorm常用快捷键
  3. 【Tiny4412】搭建Qt网络文件系统
  4. SpringBoot 过滤器、拦截器、监听器对比及使用场景!
  5. 文章页网址有.html,Phpcms V9文章内容页自定义HTML网址技巧
  6. PHP的学习--可变函数
  7. newlisp 注释生成文档
  8. withRouter有什么用?干嘛用?为啥要用它啊???一分钟理解!
  9. 年终工作总结汇报和述职报告ppt模板,内含范文可参考,精选20套可下载
  10. 人人都说风口的二次元还是门好生意吗?——动漫市场分析
  11. 3D~RPG游戏的制作
  12. 文件管理之文件和文件系统
  13. C++PrimePlus第5章编程练习答案及运行结果
  14. java 内部接口 内部类_Java接口/内部类
  15. 2020.5.12第一次博客
  16. VSS2005的配置
  17. 太戈编程 1072. 优惠促销 答案及讲解
  18. iOS内购-iap-In-App Purchases-开发及注意事项
  19. 云呐数据备份|什么是离线磁带设备
  20. 专业性体育平台——虎扑的发展与创新的思考(第五次课后作业)

热门文章

  1. mybatis 自定义插件的使用
  2. mysql免安装版的下载与安装
  3. 20162314 Experiment 3 - Sorting and Searching
  4. 30个提高Web程序执行效率的好经验
  5. 13个坏习惯让IT工作者中过劳(转)
  6. 求合作开发伙伴 .NET 中高级工程师 1-2名
  7. javascript原型和原型链
  8. [Spring-cloud-eureka]使用 gradle, spring boot,Spring cloud Eureka 搭建服务提供者
  9. 使用Doxygen + graphviz生成Unity 3d的UGUI类图
  10. 《openstack-nova》use-novaclient 创建虚拟机(createvms.py)