何时创建Java对象实例

开发 后端
类名可以作为变量的类型来使用,如果一个变量的类型是某个类,那么它将指向这个类的实例,称为对象实例。所有对象实例和它们的类型都是相容的。本文主要讲解Java中对象实例是什么时候被创建的。

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

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

请看下面代码:

  1. Date date=new Date();  
  2. em.out.println(date.getTime()); 

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

一、经验者的观点

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

请看下面代码:

  1. public class Test  
  2. {  
  3. public Test()  
  4. {  
  5. this.DoSomething();  
  6. }  
  7. private void DoSomething()  
  8. {  
  9. System.out.println("do init");  
  10. }  
  11. }  

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

二、父类构造函数

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

  1. public class BaseClass  
  2. {  
  3. public BaseClass()  
  4. {  
  5. System.out.println("create base");  
  6. }  
  7. }  
  8. public class SubClass  
  9. {  
  10. public SubClass()  
  11. {  
  12. System.out.println("create sub");  
  13. }  
  14. public static void main(String[] args)  
  15. {  
  16. new SubClass();  
  17. }  

结果是先输出create base,后输出create sub。这个结果看起来和现实世界完全一致,先有老爸,再有儿子。因此我相信有很多程序员跟我一样会认为new SubClass()的过程是:实例化BaseClass->调用BaseClass构造函数初始化->实例化SubClass->调用SubClass构造函数初始化。然而非常不幸的是,这是个错误的观点。

三、奇怪的代码

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

  1. public class BaseClass  
  2.  
  3. {  
  4.  
  5. public BaseClass()  
  6.  
  7. {  
  8.  
  9. System.out.println("create base");  
  10.  
  11. init();  
  12.  
  13. }  
  14.  
  15. protected void init()  
  16. {  
  17. System.out.println("do init");  
  18. }  
  19. }  
  20. public class SubClass  
  21. {  
  22. public SubClass()  
  23. {  
  24. System.out.println("create sub");  
  25. }  
  26. @Override 
  27. protected void init()  
  28. {  
  29. assert this!=null;  
  30. System.out.println("now the working class is:"+this.getClass().getSimpleName());  
  31. System.out.println("in SubClass");  
  32. }  
  33. public static void main(String[] args)  
  34. {  
  35. new SubClass();  
  36. }  

这段代码运行的结果是先调用父类的构造函数,再调用子类的init()方法,再调用子类的构造函数。

这是一段奇妙的代码,子类的构造函数居然不是子类第一个被执行的方法。我们早已习惯于通过super方便的调用父类的方法,但是好像从没这样尝试从父类调用子类的方法。

再次声明,这只是个示例。是为了与您一起探讨对象实例化的秘密。通过这个示例,我们再次印证了开头的观点:早在构造函数被调用之前,实例便已被创造。若该对象有父类,则早在父类的构造函数被调用之前,实例也已被创造。这让java显得有些不面向对象,原来老子儿子其实是一块儿出生的。

四、奇怪但危险的代码

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

请看下面代码:

  1. public class BaseClass {  
  2. public BaseClass()  
  3. {  
  4. System.out.println("create base");  
  5. init();  
  6. }  
  7. protected void init() {  
  8. System.out.println("in base init");  
  9. }  
  10. }  
  11. public class SubClass extends BaseClass{  
  12. int i=1024;  
  13. String s="13 leaf";  
  14. public SubClass()  
  15. {  
  16. System.out.println("create sub");  
  17. init();  
  18. }  
  19. @Override 
  20. protected void init() {  
  21. assert this!=null;  
  22. System.out.println("now the working class is:"+this.getClass().getSimpleName());  
  23. System.out.println("in SubClass");  
  24. /////////////great line/////////////////  
  25. System.out.println(i);  
  26. System.out.println(s);  
  27. }  
  28. public static void main(String[] args) {  
  29. new SubClass();  
  30. //oh!my god!!  
  31. }  
  32. }  

这段代码相比上一篇,只是在子类中添加了一些成员变量。而我们的目标正是集中在讨论成员变量初始化的问题上。

这段代码的执行顺序是:父类、子类实例化->调用父类构造函数->调用子类init()方法->调用子类构造函数->调用子类init()方法。最终的输出结果向我们揭示了成员变量初始化的秘密。

当父类构造函数调用子类的init()方法的时候。子类的成员变量统统是空的,这个空是指的低级初始化。(值类型为0,布尔类型为false,引用类型为null)。而当子类构造函数调用init()方法的时候,成员变量才真正被初始化。这是一个危险的讯息,那就是使用父类构造函数调用子类时存在成员变量未初始化的风险。

我们的讨论也到此为止了。再次回顾,总结一下实例何时被创建这个问题。我得出了以下结论:

本文到此便结束了。鉴于本人才疏学浅,若是专业术语有错误,或是哪里讲的不对,也欢迎各位高手拍砖。

【编辑推荐】

  1. Java编程之四大名著
  2. 关于java数组的深度思考
  3. Java架构设计和开发中的小技巧
  4. Java编程解析节省内存效率高的方法

 

责任编辑:于铁 来源: 中国IT实验室
相关推荐

2011-04-11 09:39:55

对象实例

2010-10-08 10:52:36

JavaScript对

2021-04-12 07:34:03

Java集合框架

2012-01-13 12:57:48

Java

2020-02-25 16:00:28

JavaScript数据技术

2013-05-27 15:38:37

Java对象C++

2021-03-17 07:49:21

Java对象内存

2019-07-24 08:34:35

Java对象数据结构

2020-10-21 14:54:02

RustGolang开发

2021-03-16 07:13:07

Java对象存储

2010-04-20 15:47:25

Oracle实例

2010-11-19 09:48:48

ORACLE创建实例

2010-09-10 15:37:44

SQL函数

2010-06-17 18:57:11

UML对象关系

2017-02-27 11:48:58

JVM源码分析Java

2011-08-08 15:43:01

MySQL索引

2024-03-21 08:55:41

享元模式对象内存

2010-06-29 18:58:23

UML面向对象技术

2009-08-20 17:22:45

C# FileSyst

2010-06-11 17:44:10

UML对象图
点赞
收藏

51CTO技术栈公众号