以前的文章中介紹了class字節碼結構組成,本文將演示如何編寫代碼解析出這些結構內容,具體能夠結合詳解class字節碼來看。java
先看效果:this
經過前文已經知道字節碼由11個大的部分組成,定義相應結構以下:spa
public final class ClassFile {
/** * 魔數 */
private final int magic;
/** * 次版本號 */
private final int minor_version;
/** * 主版本號 */
private final int major_version;
/** * 常量池 */
private final ConstantPool constant_pool;
/** * 訪問標誌 */
private final int access_flags;
/** * 當前類或接口索引,指向CONSTANT_Class_info常量 */
private final int this_class;
/** * 當前類直接父類索引,指向CONSTANT_Class_info常量 */
private final int super_class;
/** * 當前類或接口的直接父接口 */
private final int[] interfaces;
/** * 當前類的字段列表 */
private final FieldInfo[] fields;
/** * 當前類的方法列表 */
private final MethodInfo[] methods;
/** * 當前類的屬性表 */
private final AttributeInfo[] attributes;
}
複製代碼
使用DataInputStream依次讀取各個部分:code
this.magic = is.readInt();
this.minor_version = is.readUnsignedShort();
this.major_version = is.readUnsignedShort();
this.constant_pool = new ConstantPool(is);
this.access_flags = is.readUnsignedShort();
this.this_class = is.readUnsignedShort();
this.super_class = is.readUnsignedShort();
final int interfaces_count = is.readUnsignedShort();
this.interfaces = new int[interfaces_count];
for (int i = 0; i < interfaces_count; i++) {
this.interfaces[i] = is.readUnsignedShort();
}
final int fields_count = is.readUnsignedShort();
this.fields = new FieldInfo[fields_count];
for (int i = 0; i < fields_count; i++) {
this.fields[i] = new FieldInfo(is);
}
final int methods_count = is.readUnsignedShort();
this.methods = new MethodInfo[methods_count];
for (int i = 0; i < methods_count; i++) {
this.methods[i] = new MethodInfo(is);
}
final int attributes_count = is.readUnsignedShort();
this.attributes = new AttributeInfo[attributes_count];
for (int i = 0; i < attributes_count; i++) {
this.attributes[i] = Attributes.readAttributeInfo(is, ClassFile.this.constant_pool);
}
複製代碼
注: attributes在class,field,method,code屬性中都存在,其結構將在後面部分介紹orm
定義常量抽象父類,其餘常量類都繼承此類:cdn
abstract static class CPInfo {
private static final String[] tagNames = new String[]{
"", "Utf8", "Integer", "Float", "Long",
"Double", "Class", "String", "Fieldref",
"Methodref", "InterfaceMethodref", "NameAndType",
"MethodHandle", "MethodType", "InvokeDynamic"
};
/** 常量類型標記 */
private int tag;
public CPInfo(int tag) {
this.tag = tag;
}
}
複製代碼
tag值:blog
public static final short CONSTANT_Utf8 = 1;
public static final short CONSTANT_Integer = 3;
public static final short CONSTANT_Float = 4;
public static final short CONSTANT_Long = 5;
public static final short CONSTANT_Double = 6;
public static final short CONSTANT_Class = 7;
public static final short CONSTANT_String = 8;
public static final short CONSTANT_Fieldref = 9;
public static final short CONSTANT_Methodref = 10;
public static final short CONSTANT_InterfaceMethodref = 11;
public static final short CONSTANT_NameAndType = 12;
public static final short CONSTANT_MethodHandle = 15;
public static final short CONSTANT_MethodType = 16;
public static final short CONSTANT_InvokeDynamic = 18;
複製代碼
定義常量池:繼承
static class ConstantPool {
CPInfo[] pool;
}
複製代碼
解析出常量池:索引
CONSTANT_Utf8_info
, CONSTANT_Integer_info
等繼承CPInfo
接口
定義以下:
class FieldInfo {
private int access_flags;
private int name_index;
private int descriptor_index;
private AttributeInfo[] attributes;
}
複製代碼
解析以下:
this.access_flags = is.readUnsignedShort();
this.name_index = is.readUnsignedShort();
this.descriptor_index = is.readUnsignedShort();
final int attributes_count = is.readUnsignedShort();
this.attributes = new AttributeInfo[attributes_count];
for (int i = 0; i < attributes_count; i++) {
this.attributes[i] = Attributes.readAttributeInfo(is, ClassFile.this.constant_pool);
}
複製代碼
定義:
private int access_flags;
private int name_index;
private int descriptor_index;
private AttributeInfo[] attributes;
複製代碼
解析:
this.access_flags = is.readUnsignedShort();
this.name_index = is.readUnsignedShort();
this.descriptor_index = is.readUnsignedShort();
final int attributes_count = is.readUnsignedShort();
this.attributes = new AttributeInfo[attributes_count];
for (int i = 0; i < attributes_count; i++) {
this.attributes[i] = Attributes.readAttributeInfo(is, ClassFile.this.constant_pool);
}
複製代碼
定義抽象基類:
static abstract class AttributeInfo {
public static final String AnnotationDefault = "AnnotationDefault";
public static final String BootstrapMethods = "BootstrapMethods";
public static final String Code = "Code";
public static final String ConstantValue = "ConstantValue";
public static final String Deprecated = "Deprecated";
public static final String EnclosingMethod = "EnclosingMethod";
public static final String Exceptions = "Exceptions";
public static final String InnerClasses = "InnerClasses";
public static final String LineNumberTable = "LineNumberTable";
public static final String LocalVariableTable = "LocalVariableTable";
public static final String LocalVariableTypeTable = "LocalVariableTypeTable";
public static final String MethodParameters = "MethodParameters";
public static final String RuntimeVisibleAnnotations = "RuntimeVisibleAnnotations";
public static final String RuntimeInvisibleAnnotations = "RuntimeInvisibleAnnotations";
public static final String RuntimeVisibleParameterAnnotations = "RuntimeVisibleParameterAnnotations";
public static final String RuntimeInvisibleParameterAnnotations = "RuntimeInvisibleParameterAnnotations";
public static final String RuntimeVisibleTypeAnnotations = "RuntimeVisibleTypeAnnotations";
public static final String RuntimeInvisibleTypeAnnotations = "RuntimeInvisibleTypeAnnotations";
public static final String Signature = "Signature";
public static final String SourceDebugExtension = "SourceDebugExtension";
public static final String SourceFile = "SourceFile";
public static final String StackMapTable = "StackMapTable";
public static final String Synthetic = "Synthetic";
protected int attribute_name_index;
protected int attribute_length;
public AttributeInfo(DataInputStream is, int attribute_name_index) throws IOException {
this.attribute_name_index = attribute_name_index;
this.attribute_length = is.readInt();
}
public String getName(ConstantPool cp) {
ConstantPool.CONSTANT_Utf8_info cpInfo = (ConstantPool.CONSTANT_Utf8_info) cp.pool[attribute_name_index];
return String.format("%s(#%s)", cpInfo.getName(cp), attribute_name_index);
}
}
複製代碼
主要將各屬性共有的attribute_name_index
, attribute_length
提取到基類中,23個常量字符串是attribute_name_index
引用的常量CONSTANT_Utf8_info
的值,根據它斷定屬性類型
讀取具體屬性:
根據attribute_name就能夠解析出相應屬性了, 如Code_attribute
:
static class Code extends AttributeInfo {
private int max_stack;//u2
private int max_locals;//u2
private int code_length;//u4
private byte[] code;
private int exception_table_length;
private ExceptionTable[] exception_table;
private int attributes_count;
private AttributeInfo[] attributes;
public Code(DataInputStream is, int attribute_name_index, ConstantPool cp) throws IOException {
super(is, attribute_name_index);
max_stack = is.readUnsignedShort();
max_locals = is.readUnsignedShort();
code_length = is.readInt();
code = new byte[code_length];
is.read(code);
exception_table_length = is.readUnsignedShort();
exception_table = new ExceptionTable[exception_table_length];
for (int i = 0; i < exception_table_length; i++) {
exception_table[i] = new ExceptionTable(is.readUnsignedShort(), is.readUnsignedShort(),
is.readUnsignedShort(), is.readUnsignedShort());
}
attributes_count = is.readUnsignedShort();
attributes = new AttributeInfo[attributes_count];
for (int i = 0; i < attributes_count; i++) {
attributes[i] = Attributes.readAttributeInfo(is, cp);
}
}
class ExceptionTable {
int start_pc;
int end_pc;
int handler_pc;
int catch_type;
public ExceptionTable(int start_pc, int end_pc, int handler_pc, int catch_type) {
this.start_pc = start_pc;
this.end_pc = end_pc;
this.handler_pc = handler_pc;
this.catch_type = catch_type;
}
}
}
複製代碼
完!能夠看出字節碼結構還算簡單,就是類型比較多,比較繁雜而已。
完整代碼關注訂閱號,回覆 class
便可獲取