热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

对象实例化的顺序

对象实例化的顺序分类:java,基础日期:2012-11-08作者:ticmyhttp:www.ticmy.com?p302

对象实例化的顺序

分类:java, 基础日期:2012-11-08作者:ticmy

  http://www.ticmy.com/?p=302
 
 

创建一个对象大概有以下几种方式:
1、通过new关键字,如new Object();
2、通过某些反射类的newInstance方法,如Class#newInstance、Constructor#newInstance;
3、如果对象是Cloneable的,通过clone方法;
4、通过ObjectInputStream#readObject反序列化;
以上是通过java程序可以创建出对象的方式,jvm中还有一些隐式创建对象的地方,譬如:
1、启动一个类,main方法的参数String数组是隐式创建的,如果指定了一个或多个String对象,还要创建这些String对象;
2、读入一个class二进制数据的时候,创建一个与之对应的java.lang.Class类的对象;
3、在使用“+”进行字符串变量连接时,可能会创建StringBuffer/StringBuilder对象;
如此等等。

那么,在程序中通过new或newInstance创建对象(后面说创建对象均指这两种方式)的时候,构造方法、实例变量、父类构造方法、父类实例变量等的执行顺序是怎样的?

在创建对象的时候,首先会分配内存,此时所有实例变量均为默认值,然后做初始化实例变量、构造方法调用等操作。对于类变量,在创建对象之前,加载类的时候已经做掉了,这里为避免干扰,忽略掉。

先来一个例子:

public class Init {
     public static void main(String[] args) throws Exception {
         S s = new S();
         System.out.println(s.getV2());
     }
}
 
class P {
     private int v1 = 5 ;
     private int v2 = getV1();
     public P() throws Exception {
         System.out.println( "P" );
     }
     
     public int getV1() {
         return v1;
     }
     public int getV2() {
         return v2;
     }
}
 
class S extends P {
     private int value1 = 4 ;
     
     public int getV1() {
         return value1;
     }
     
     public S() throws Exception {
         this ( "S()" );
     }
     
     public S(String msg) throws Exception {
         System.out.println(msg);
     }
     
     public S( int v) throws Exception {
         super ();
         System.out.println( "abc" );
     }
}

执行结果如下:

P
S()
0

调用s.getV2()的值为0,是为什么呢,内部的机制是怎样的?先来了解点其它内容。
在编译代码的时候,会为每个构造方法生成一个对应的方法,方法名叫。但并不是直接将构造方法体作为方法的内容,它有这样的规则:

如果构造方法中的第一条语句是通过this调用本类的其它构造方法,如类S的第一个构造方法,其完整的构造方法体就是对应的方法的方法体。编译器不会为其添加一个super调用了。

如果构造方法中的第一条语句不是通过this调用本类的其它构造方法,会按以下内容与顺序组成方法体:
1、超类方法的调用。如果是显式的调用了超类构造方法,将会使用对应的超类方法,如果没有写,编译器会生成一个超类无参方法的调用;
2、实例变量初始化代码,按实例变量在类中出现的顺序;
3、构造方法中的其它方法体(如果第一句是super(…)调用,则不包含该句)。

如果构造方法中包含super(…)或this(…)调用,那么它们只能作为该构造方法的第一条语句,也就是说连try…catch都不可以有。因为必须为第一条语句,所以super(…)和this(…)调用是不会出现在一起的。

用javap -c S反编译获得各方法的字节码如下:

public S() throws java.lang.Exception;
Code:
    0 : aload_0
    1 : ldc           # 2                  // String S()
    3 : invokespecial # 3                  // Method "":(Ljava/lang/String;)V
    6 : return
 
public S(java.lang.String) throws java.lang.Exception;
Code:
    0 : aload_0
    1 : invokespecial # 4                  // Method P."":()V
    4 : aload_0
    5 : iconst_4
    6 : putfield      # 1                  // Field value1:I
    9 : getstatic     # 5                  // Field java/lang/System.out:Ljava/io/PrintStream;
   12 : aload_1
   13 : invokevirtual # 6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
   16 : return
 
public S( int ) throws java.lang.Exception;
Code:
    0 : aload_0
    1 : invokespecial # 4                  // Method P."":()V
    4 : aload_0
    5 : iconst_4
    6 : putfield      # 1                  // Field value1:I
    9 : getstatic     # 5                  // Field java/lang/System.out:Ljava/io/PrintStream;
   12 : ldc           # 7                  // String abc
   14 : invokevirtual # 6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
   17 : return

可以看出,在类S的第一个构造方法s()中,字节码表现出的只是调用了本类的(String)方法;从第二个构造方法S(String)其对应的字节码中可以看出,虽然没有用super(…)显式的调用超类的构造方法,编译器还是生成了偏移量为0,1的指令用于调用超类的方法,偏移量为4,5,6的指令是给变量value1赋值为4,后面即为构造方法体——唯一的一条打印语句;再看S(int)对应的方法,构造方法的第一条语句为super(…)调用,对应着中的偏移量为0,1的指令;偏移量为4,5,6的指令是为实例变量value1赋值为4;最后是打印字符串abc。

再看看P反编译后构造方法体:

public P() throws java.lang.Exception;
Code:
    0 : aload_0
    1 : invokespecial # 1                  // Method java/lang/Object."":()V
    4 : aload_0
    5 : iconst_5
    6 : putfield      # 2                  // Field v1:I
    9 : aload_0
   10 : aload_0
   11 : invokevirtual # 3                  // Method getV1:()I
   14 : putfield      # 4                  // Field v2:I
   17 : getstatic     # 5                  // Field java/lang/System.out:Ljava/io/PrintStream;
   20 : ldc           # 6                  // String P
   22 : invokevirtual # 7                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
   25 : return

偏移量为0,1的指令调用Object的方法,也就是类P的超类;指令4,5,6为v1赋值为5,指令9,10,11,14是为实例变量v2获取值并赋值;剩余的是打印字符串P。

从上面几个构造方法与对应的方法分析中可以看出,它们都是符合前文所述的规则的。那么对于本文开头的例子v2的值为何为0就好分析了:
1、new S()的时候调用的是S类的()方法,它去调用了本类的(String)方法,在这个中,先去调用超类,也就是P的()方法,P的()方法又去调用P的超类Object的()方法,然后去给v1,v2赋值,v2赋值调用的是getV1(),而这个方法是一个多态方法,它会去调用new对象的那个类的getV1方法,也就是S中的getV1,S中的getV1返回的是value1的值,而此时value1赋值操作还没有做呢,所以返回的是它的默认值0.

如此,整个创建对象期间的顺序就一清二楚了。


推荐阅读
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • Java自带的观察者模式及实现方法详解
    本文介绍了Java自带的观察者模式,包括Observer和Observable对象的定义和使用方法。通过添加观察者和设置内部标志位,当被观察者中的事件发生变化时,通知观察者对象并执行相应的操作。实现观察者模式非常简单,只需继承Observable类和实现Observer接口即可。详情请参考Java官方api文档。 ... [详细]
  • 海马s5近光灯能否直接更换为H7?
    本文主要介绍了海马s5车型的近光灯是否可以直接更换为H7灯泡,并提供了完整的教程下载地址。此外,还详细讲解了DSP功能函数中的数据拷贝、数据填充和浮点数转换为定点数的相关内容。 ... [详细]
  • 基于Socket的多个客户端之间的聊天功能实现方法
    本文介绍了基于Socket的多个客户端之间实现聊天功能的方法,包括服务器端的实现和客户端的实现。服务器端通过每个用户的输出流向特定用户发送消息,而客户端通过输入流接收消息。同时,还介绍了相关的实体类和Socket的基本概念。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • 判断数组是否全为0_连续子数组的最大和的解题思路及代码方法一_动态规划
    本文介绍了判断数组是否全为0以及求解连续子数组的最大和的解题思路及代码方法一,即动态规划。通过动态规划的方法,可以找出连续子数组的最大和,具体思路是尽量选择正数的部分,遇到负数则不选择进去,遇到正数则保留并继续考察。本文给出了状态定义和状态转移方程,并提供了具体的代码实现。 ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • 如何搭建Java开发环境并开发WinCE项目
    本文介绍了如何搭建Java开发环境并开发WinCE项目,包括搭建开发环境的步骤和获取SDK的几种方式。同时还解答了一些关于WinCE开发的常见问题。通过阅读本文,您将了解如何使用Java进行嵌入式开发,并能够顺利开发WinCE应用程序。 ... [详细]
author-avatar
久居我心入我怀
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有