java class 字节码文件结构详解-亚博电竞手机版

class字节码中有两种数据类型:

  1. 字节数据直接量:这是基本的数据类型。共细分为u1、u2、u4、u8四种,分别代表连续的1个字节、2个字节、4个字节、8个字节组成的整体数据。
  2. 表:表是由多个基本数据或其他表,按照既定顺序组成的大的数据集合。表是有结构的,它的结构体现在,组成表的成分所在的位置和顺序都是已经严格定义好的。

class字节码总体结构如下:

具体详解请参考http://www.blogjava.net/dlevin/archive/2011/09/05/358033.html

我在这里要说明几个细节问题:

  1. 为什么说常量表的数量是constant_pool_count-1,且索引从1开始而不是0。其实根本原因在于,索引为0也是一个常量(保留常量),只不过它不存在常量表,这个常量就对应null值。因此加上这个系统保留常量,常量个数共为constant_pool_count个,但是常量表数量要减1。
  2. 在常量池中,如果存在long型或double型字面量,它们会占用两个连续索引。比如:假设一个类中只有一个int型字面量1和一个double型字面量1(当然这种假设是不可能的,因为总会有类名字面量等),则常量池个数为3,而不是2。这正是因为double字面量占用了两个连续的索引。

接下来,贴出一个小demo来展示如何读取字节码:

classparser负责把握class字节码整体结构的解析。

package com.lixin;  import java.io.ioexception; import java.io.inputstream;  public class classparser {  	private inputstream in;  	public classparser(inputstream in) { 		this.in = in; 	}  	public void parse() throws ioexception { 		// 魔数 		magicnumber(); 		// 主次版本号 		version(); 		// 常量池 		constantpool(); 		// 类或接口修饰符 		accessflag(); 		// 继承关系(当前类、父类、父接口) 		inheritence(); 		// 字段集合 		fieldlist(); 		// 方法集合 		methodlist(); 		// 属性集合 		attributelist(); 	}  	private void attributelist() throws ioexception { 		line(); 		int attrlength = streamutils.read2(in); 		system.out.println("共有" attrlength "个属性"); 		for (int i=0;i 

constpoolparser负责常量池的解析(因为常量池表较多,且数据量也较大,因此单独拉出来解析)

package com.lixin;  import java.io.ioexception; import java.io.inputstream;  public class constpoolparser {  	public static final int utf8_info = 1; 	public static final int integer_info = 3; 	public static final int float_info = 4; 	public static final int long_info = 5; 	public static final int double_info = 6; 	public static final int class_info = 7; 	public static final int string_info = 8; 	public static final int fieldref_info = 9; 	public static final int methodref_info = 10; 	public static final int interfacemethodref_info = 11; 	public static final int nameandtype_info = 12; 	public static final int methodhandle_info = 15; 	public static final int methodtype_info = 16; 	public static final int invokedynamic_info = 18;  	private inputstream in;  	public constpoolparser(inputstream in) { 		this.in = in; 	}  	public void constpool() throws ioexception { 		line(); 		int length = streamutils.read2(in); 		system.out.println("共有" length "个常量"); 		boolean doublebytes = false; 		for (int i = 1; i < length; i  ) { 			if (doublebytes) { 				doublebytes = false; 				continue; 			} 			line(); 			system.out.println("常量索引:" i); 			int flag = streamutils.read1(in); //			system.out.println("标志:" flag);  			switch (flag) { 			case utf8_info: 				utf8info(); 				continue; 			case integer_info: 				integerinfo(); 				continue; 			case float_info: 				floatinfo(); 				continue; 			case long_info: 				doublebytes = true; 				longinfo(); 				continue; 			case double_info: 				doublebytes = true; 				doubleinfo(); 				continue; 			case class_info: 				classinfo(); 				continue; 			case string_info: 				stringinfo(); 				continue; 			case fieldref_info: 				fieldrefinfo(); 				continue; 			case methodref_info: 				methodrefinfo(); 				continue; 			case interfacemethodref_info: 				interfacemethodrefinfo(); 				continue; 			case nameandtype_info: 				nameandtypeinfo(); 				continue; 			case methodhandle_info: 				methodhandleinfo(); 				continue; 			case methodtype_info: 				methodtypeinfo(); 				continue; 			case invokedynamic_info: 				invokedynamicinfo(); 				continue; 			default: 				system.err.println(flag); 				throw new runtimeexception("unknown"); 			} 		} 	}  	private void line() { 		system.out.println("----------------------"); 	}  	private void utf8info() throws ioexception { 		int length = streamutils.read2(in); 		byte[] buf = streamutils.read(in, length); 		string s = new string(buf,0,buf.length); 		system.out.println("utf8info表:"); 		system.out.println("值:" s); 	}  	private void integerinfo() throws ioexception { 		system.out.println("integerinfo表:"); 		int value = streamutils.read4(in); 		system.out.println("值:" value); 	}  	private void floatinfo() throws ioexception { 		system.out.println("floatinfo表:"); 		int value = streamutils.read4(in); 		float f = float.intbitstofloat(value); 		system.out.println("值:" f); 	}  	private void longinfo() throws ioexception { 		system.out.println("longinfo表:"); 		long value = streamutils.read8(in); 		system.out.println("值:" value); 	}  	private void doubleinfo() throws ioexception { 		system.out.println("doubleinfo表:"); 		long value = streamutils.read8(in); 		double d = double.longbitstodouble(value); 		system.out.println("值:" d);  	}  	private void classinfo() throws ioexception { 		system.out.println("classinfo表:"); 		int index = streamutils.read2(in); 		system.out.println("index:"   index); 	}  	private void stringinfo() throws ioexception { 		system.out.println("stringinfo表:"); 		int index = streamutils.read2(in); 		system.out.println("index:"   index); 	}  	private void fieldrefinfo() throws ioexception { 		int classindex = streamutils.read2(in); 		int nameandtypeindex = streamutils.read2(in); 		system.out.println("fieldrefinfo表:"); 		system.out.println("classindex:"   classindex); 		system.out.println("nameandtypeindex:"   nameandtypeindex); 	}  	private void methodrefinfo() throws ioexception { 		int classindex = streamutils.read2(in); 		int nameandtypeindex = streamutils.read2(in); 		system.out.println("methodrefinfo表:"); 		system.out.println("classindex:"   classindex); 		system.out.println("nameandtypeindex:"   nameandtypeindex); 	}  	private void interfacemethodrefinfo() throws ioexception { 		int classindex = streamutils.read2(in); 		int nameandtypeindex = streamutils.read2(in); 		system.out.println("interfacemethodrefinfo表:"); 		system.out.println("classindex:"   classindex); 		system.out.println("nameandtypeindex:"   nameandtypeindex); 	}  	private void nameandtypeinfo() throws ioexception { 		int nameindex = streamutils.read2(in); 		int typeindex = streamutils.read2(in); 		system.out.println("nameandtypeinfo表:"); 		system.out.println("nameindex:"   nameindex); 		system.out.println("typeindex:"   typeindex); 	}  	private void methodhandleinfo() throws ioexception { 		int referencekind = streamutils.read1(in); 		int referenceindex = streamutils.read2(in); 		system.out.println("methodhandleinfo表:"); 		system.out.println("referencekind:" referencekind); 		system.out.println("referenceindex:" referenceindex); 	}  	private void methodtypeinfo() throws ioexception { 		system.out.println("methodtypeinfo表:"); 		int descriptorindex = streamutils.read2(in); 		system.out.println("descriptorindex:" descriptorindex); 	}  	private void invokedynamicinfo() throws ioexception { 		int bootstrapmethodattrindex = streamutils.read2(in); 		int nameandtypeindex = streamutils.read2(in); 		system.out.println("bootstrapmethodattrindex:" bootstrapmethodattrindex); 		system.out.println("nameandtypeindex:" nameandtypeindex); 	} }

streamutils负责从输入字节流中读取数据

package com.lixin;  import java.io.ioexception; import java.io.inputstream;  public class streamutils {  	public static int read1(inputstream in) throws ioexception { 		return in.read() & 0xff; 	}  	public static int read2(inputstream in) throws ioexception{ 		return (read1(in) << 8) | read1(in); 	}  	public static int read4(inputstream in) throws ioexception { 		return (read2(in) <<16) | read2(in); 	}  	public static long read8(inputstream in) throws ioexception { 		long high = read4(in) & 0xffffffffl; 		long low  = read4(in) & 0xffffffffl; 		return (high << 32) | (low); 	}  	public static byte[] read(inputstream in,int length) throws ioexception { 		byte[] buf = new byte[length]; 		in.read(buf, 0, length); 		return buf; 	} }

testclass为待解析的目标类,读者可以任意改写此类来多做实验

package com.lixin;  public class testclass {  	private int a = 5; 	protected char c = 'c'; 	double x = 1.1; 	long y = 111;  	public void show() {  	} }

测试方法入口:

package com.lixin;  import java.io.inputstream;  /**  * 程序入口  * @author lixin  *  */ public class app {  	public static void main(string[] args) throws exception { 		inputstream in = class.class.getresourceasstream("/com/lixin/testclass.class"); 		classparser parser = new classparser(in); 		parser.parse(); 	}  }

最后,我们可以使用jdk中的javap进行字节码反编译,来对比我们的读取与反编译结果差别,用于查错。

javap -v testclass.class >./out.txt

展开全文
内容来源于互联网和用户投稿,文章中一旦含有亚博电竞手机版的联系方式务必识别真假,本站仅做信息展示不承担任何相关责任,如有侵权或涉及法律问题请联系亚博电竞手机版删除

最新文章

网站地图