java class 字节码文件结构详解-亚博电竞手机版
java学习
2020年03月27日 23:25
1
class字节码中有两种数据类型:
- 字节数据直接量:这是基本的数据类型。共细分为u1、u2、u4、u8四种,分别代表连续的1个字节、2个字节、4个字节、8个字节组成的整体数据。
- 表:表是由多个基本数据或其他表,按照既定顺序组成的大的数据集合。表是有结构的,它的结构体现在,组成表的成分所在的位置和顺序都是已经严格定义好的。
class字节码总体结构如下:
具体详解请参考http://www.blogjava.net/dlevin/archive/2011/09/05/358033.html
我在这里要说明几个细节问题:
- 为什么说常量表的数量是constant_pool_count-1,且索引从1开始而不是0。其实根本原因在于,索引为0也是一个常量(保留常量),只不过它不存在常量表,这个常量就对应null值。因此加上这个系统保留常量,常量个数共为constant_pool_count个,但是常量表数量要减1。
- 在常量池中,如果存在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;iconstpoolparser负责常量池的解析(因为常量池表较多,且数据量也较大,因此单独拉出来解析)
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
展开全文