读取class文件

2,317 阅读3分钟

之前的文章中介绍了class字节码结构组成,本文将演示如何编写代码解析出这些结构内容,具体可以结合详解class字节码来看。

先看效果:

整体结构

通过前文已经知道字节码由11个大的部分组成,定义相应结构如下:

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依次读取各个部分:

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属性中都存在,其结构将在后面部分介绍

ConstantPool

定义常量抽象父类,其他常量类都继承此类:

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值:

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

FieldInfo

定义如下:

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);
}
MethodInfo

定义:

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);
}
Attribute

定义抽象基类:

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 即可获取