正在阅读:深入剖析java类的构造方式深入剖析java类的构造方式

2004-08-27 10:14 出处:CSDN 作者:chensheng913 责任编辑:linjixiong


  6 putfield #15 <Field int cm1>
  9 getstatic #20 <Field java.io.PrintStream out>
  12 ldc #4 <String "Child's self-define constructor">
  14 invokevirtual #21 <Method void println(java.lang.String)>
  17 return

  Method int cmethod()
  0 getstatic #20 <Field java.io.PrintStream out>
  3 ldc #3 <String "Child's method">
  5 invokevirtual #21 <Method void println(java.lang.String)>
  8 iconst_3
  9 ireturn

  Method void staticmethod()
  0 getstatic #20 <Field java.io.PrintStream out>
  3 ldc #6 <String "Child's staticmethod">
  5 invokevirtual #21 <Method void println(java.lang.String)>
  8 return

 

  请仔细浏览一下这个输出并和源代码比较一下。

  下面解释如何根据这个输出得到类实例的实际的构造顺序,在开始说明前先解释一下输出的语句的格式,语句中最前面的一个数字是指令的偏移值,这个我们在此可以不管,第二项是指令助记符,可以从字面上大致看出指令的意思。

  例如 getstatic 指令将一个静态成员压入一个称为操作数堆栈(后续的指令就可以引用这个数据结构中的成员)的数据结构,而 invokevirtual 指令是调用java虚拟机方法,第三项是操作数(#号后面跟一个数字,实际上是类的成员的标记),有些指令没有这一项,因为有些指令如同汇编指令中的某些指令一样是不需要操作数的(可能是操作数是隐含的或者根本就不需要),这是java中的一个特色。

  如果你直接检查字节码,你会看到成员信息没有直接嵌入指令而是像所有由java类使用的常量那样存储在一个共享池中,将成员信息存储在一个常量池中可以减小字节码指令的大小,因为指令只需要存储常量池中的一个索引而不是整个常量。

  需要说明的是常量池中的项目的顺序是和编译器相关的,因此在你的环境中看到的可能和我上面给出的输出不完全一样,第四项是对前面的操作数的说明,实际的字节码中也是没有的,根据这个你能很清楚的得到实际上使用的是哪个成员或者调用的是哪个方法,这也是javap为我们提供的便利。

  说完上面这些你现在应该很容易看懂上面的结果和下面将要叙述的内容了。其它更进一步的有关java字节码的信息请自己查找资料。

  先看看最开始的部分,很像一个标准的c++类的声明,确实如此。成员声明的后面没有了成员初始化赋值语句和初始化块,那么这些语句何时执行的呢?先不要急,继续往下看。

  第二块,是一个Method static {},对比看看第一部分,它被处理为一个静态的方法(从前面的Method可以看出),这就是源代码中的静态初始化块,从后面的语句可以看出它执行的就是System.out.println("Child's static initialize block")语句,由于这个方法是没有方法名的,所以它不能被显式的调用,它在何处调用后面会有叙述。


察看评论详细内容 我要发表评论
作者笔名 简短内容 发表时间
:

键盘也能翻页,试试“← →”键

关注我们

最新资讯离线随时看 聊天吐槽赢奖品