深入理解Java Class文件格式(七)(二)

2014-11-24 00:35:11 · 作者: · 浏览: 4
在编译时向class文件增加额外的方法, 也就是说, class文件中的方法的数量可能多于源文件中由用户定义的方法。 举例来说: 如果当前类没有定义构造方法, 那么编译器会增加一个无参数的构造函数 ; 如果当前类或接口中定义了静态变量, 并且使用初始化表达式为其赋值, 或者定义了static静态代码块, 那么编译器在编译的时候会默认增加一个静态初始化方法 。 位于methods_count下面的数据叫做methods , 可以把它看做一个数组, 数组中的每一项是一个method_info 。这个数组中一共有methods_count个method_info , 每个method_info 都是对一个方法的描述。 下面我们详细讲解method_info 的结构。 每个method_info 的结构如下, 几乎和field_info的结构是一样的:
\

(1)access_flags

其中access_flags占两个字节, 描述的是方法的访问标志信息。 这里就不在详细介绍了, 下面给出一张表格(该表格来自《深入Java虚拟机》):

标志位名称 标志值 设定含义 设定者
ACC_PUBLIC 0x0001 方法设为public 类和接口
ACC_PRIVATE 0x0002 方法设为private
ACC_PROTECTED 0x0004 方法设为protected
ACC_STATIC 0x0008 方法设为static
ACC_FINAL 0x0010 方法设为final
ACC_SYNCHRONIZED 0x0020 方法设为sychronized
ACC_NATIVE 0x0100 方法设为native
ACC_ABSTRACT 0x0400 方法设为abstract 类和接口
ACC_STRICT 0x0800 方法设为strictFP 类和接口的 方法


(2)name_index

access_flags下面的两个字节是name_index, 这是一个指向常量池的索引, 它描述的是当前方法的方法名。 这个索引指向常量池中的一个CONSTANT_Utf8_info数据项。 这个CONSTANT_Utf8_info数据项中存放的字符串就是当前方法的方法名。


(3)descriptor_index

name_index下面的两个字节叫做descriptor_index , 它同样是一个指向常量池的索引, 它描述的是当前方法的描述符。 这个索引指向常量池中的一个CONSTANT_Utf8_info数据项。 这个CONSTANT_Utf8_info数据项中存放的字符串就是当前方法的描述符(关于方法描述符, 在前面的博客中已经有过详细的讲解, 如果不明白, 请参考前面的博客: 深入理解Java Class文件格式(二))。

(4)attributes_count和attributes

descriptor_index 下面是attributes_count和attributes 。 这是对当前方法所具有的属性的描述。 这里的属性和源文件中的属性不是同一个概念, 在源文件测层面中, 属性是字段的另一种叫法, 希望读者不要疑惑。读者也不要轻视class文件中的属性, 这些属性可以描述很多的信息。 我们会在后面的文章中进行介绍。
attributes_count表示这个字段有几个属性。attributes 可以看成一个数组, 数组中的每一项都是一个attribute_info , 每个attribute_info 表示一个属性, 数组中一共有attributes_count个属性。可以出现在method_info 中的属性有三种, 分别是Code, Deprecated, Exceptions 和Synthetic。 在这几个属性中, 尤其是Code和Exceptions 非常重要, 这两个属性对于在class文件中完整描述一个方法起着至关重要的作用, 其中Code属性中存放方法的字节面指令,Exceptions 属性是对方法声明中抛出的异常的描述 。 这两属性以及其他一些属性, 会在下一篇文章中详细介绍, 敬请关注。

介绍完了每个method_info的结构, 下面我们以代码来说明, 还是使用上面的源码:
package com.jg.zhang;

public class Programer extends Person{

	
	private Computer computer;
	
	public Programer(Computer computer){
		this.computer = computer;
	}
	
	public void doWork(){
		computer.calculate();
	}
}

反编译之后, 常量池中会有如下信息(这里省略了大部分无关信息):

Constant pool:

.........

   #7 = Utf8               
    
     
   #8 = Utf8               (Lcom/jg/zhang/Computer;)V

.........

  #12 = Utf8               ()V

.........

  #19 = Utf8               doWork

{

.........

  public com.jg.zhang.Programer(com.jg.zhang.Computer);
    flags: ACC_PUBLIC

.........

  public void doWork();
    flags: ACC_PUBLIC

.........
}
    

由反编译结果可以看出, 该类中定义了两个方法, 其中一个是构造方法, 一个是doWork方法, 且这两个方法都是public的。 这两个方法的描述信息都存放在常量池。 其中第7项的CONSTANT_Utf8_info为构造方法的方法名, 第8项的CONSTANT_Utf8_info为构造方法的方法描述符, 第19项的CONSTANT_Utf8_info为doWork方法的方法名, 第12项的CONSTANT_Utf8_info为doWork方法的方法描述符。
根据常量池中的信息, 可以得出如下的示意图, 该示意图形象的说明了class文件中的method_info是如何引用常量池中的数据项来描述当前类中定义的方法的。 图中虚线范围内表示常量池所在的区域:
\


< http://www.2cto.com/kf/ware/vc/" target="_blank" class="keylink">vcD4KPGgxPtfcveE8L2gxPgo8YnI+Cgq1vbTLzqrWuaOsIM7Sw8e+zb3pydzN6sHLY2xhc3POxLz+1tC1xGZpZWxkc7rNbWV0aG9kc6OsIL340NDSu8/C19y94aGjIAo8YnI+CgogZmllbGRzyse21LWxx7DA4NbQtqjS5bXE19a2zrXEw+jK9qOsIMbk1tDDv7j219a2zsq508PSu7j2ZmllbGRfaW5mb7Htyr6jrCBmaWVsZHPW0NPQZmllbGRzX2NvdW50uPZmaWVsZF9pbmZvoaMKPGJyPgoKbWV0aG9kc8rHttS1scewwOC78tXfvdO/2tbQyfnD97XEt723qLXEw+jK9qOsIMbk1tDDv7j2t723qMq508PSu7j2bWV0aG9kX2luZm+x7cq+o6wgbWV0aG9kc9bQ09BtZXRob2RzX2NvdW50uPZtZXRob2RfaW5mb6GjIDxicj4KCjxicj4KCtTaz8LSu8aqsqm/zdbQo6wgvau74b3pydxjbGFzc87EvP7W0LXEuPe49sr00NSjrCC+tMfrudjXoqGjCjxicj4KCjxicj4KCjxicj4KCjxicj4KCgo8cCBjbGFzcz0="cjk">更多关于深入理解Java的文章, 请关注我的专栏 : http://blog.csdn.net/column/details/zhangjg-java-blog.html

更多关于Java和Android等其他技术的文章, 请关注我的博客: http://blog.csdn.net/zhangjg_blog