前面说到读取魔数和版本号,今天讨论下读取常量池,我们可以借助javap -verbose
的命令查看对应的class文件的内容。
javap -verbose AppMain.class >>AppMain.txt
输出的内容:
代码语言:javascript复制Classfile /Users/fuwei/work/rzclassreader/target/classes/org/rz/AppMain.class
Last modified 2020-3-20; size 2972 bytes
MD5 checksum fbb55a90b8c5daddb71f86a69f9baa6d
Compiled from "AppMain.java"
public class org.rz.AppMain
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #37.#97 // java/lang/Object."<init>":()V
#2 = Long 34122202019029l
#4 = Fieldref #11.#98 // org/rz/AppMain.indentityCardNo:J
...
#146 = Utf8 Ljava/io/PrintStream;
#147 = Utf8 java/io/PrintStream
#148 = Utf8 println
由于篇幅的原因不展示全部,从上面的信息可以看出,我们的常量池有148个常量,每一个常量的类型不同,分别有Methodref
,Long
和Utf-8
等等。
根据JVM的规范,常量池的基本结构如下:
代码语言:javascript复制cp_info {
u1 tag;
u1 info[];
}
u1代表一个标记为常量的基本类型的标记,对应的数据类型如下:
代码语言:javascript复制public class ConstantPoolType {
public static final int CONSTANT_Utf8 = 1;
public static final int CONSTANT_Integer = 3;
public static final int CONSTANT_Float = 4;
public static final int CONSTANT_Long = 5;
public static final int CONSTANT_Double = 6;
public static final int CONSTANT_Class = 7;
public static final int CONSTANT_String = 8;
public static final int CONSTANT_Fieldref = 9;
public static final int CONSTANT_Methodref = 10;
public static final int CONSTANT_InterfaceMethodref = 11;
public static final int CONSTANT_NameAndType = 12;
public static final int CONSTANT_MethodHandle = 15;
public static final int CONSTANT_MethodType = 16;
public static final int CONSTANT_InvokeDynamic = 18;
}
基于上面的认识,可以简单的设计成一个switch-case
的模式,对应代码:
private CPInfo[] cpInfos;
private final int count;
public ConstantPool(ClassReadCursor cursor) throws IOException {
count = cursor.readUnsignedShort();
cpInfos = new CPInfo[count];
init(cursor);
}
public void init(ClassReadCursor cursor) throws IOException {
for (int i = 1; i < count ; i ) {
int tag = cursor.readUnsignedByte();
switch (tag) {
case CONSTANT_Utf8: {
cpInfos[i] = new CONSTANT_Utf8_info(cursor);
break;
}
case CONSTANT_Integer: {
cpInfos[i] = new CONSTANT_Integer_info(cursor);
break;
}
case CONSTANT_Float: {
cpInfos[i] = new CONSTANT_Float_info(cursor);
break;
}
case CONSTANT_Long: {
cpInfos[i] = new CONSTANT_Long_info(cursor);
i ;
break;
}
case CONSTANT_Double: {
cpInfos[i] = new CONSTANT_Double_info(cursor);
i ;
break;
}
case CONSTANT_String: {
cpInfos[i] = new CONSTANT_String_info(cursor);
break;
}
case CONSTANT_Fieldref: {
cpInfos[i] = new CONSTANT_Fieldref_info(cursor);
break;
}
case CONSTANT_Methodref: {
cpInfos[i] = new CONSTANT_Methodref_info(cursor);
break;
}
case CONSTANT_InterfaceMethodref: {
cpInfos[i] = new CONSTANT_InterfaceMethodref_info(cursor);
break;
}
case CONSTANT_NameAndType: {
cpInfos[i] = new CONSTANT_NameAndType_info(cursor);
break;
}
case CONSTANT_Class: {
cpInfos[i] = new CONSTANT_Class_info(cursor);
break;
}
case CONSTANT_MethodHandle: {
cpInfos[i] = new CONSTANT_MethodHandle_info(cursor);
break;
}
case CONSTANT_MethodType: {
cpInfos[i] = new CONSTANT_MethodType_info(cursor);
break;
}
}
}
}
其中Long
和Double
类型占用两个字节,索引需要加1。 由于篇幅的原因不能把每一个解析的方法都贴出来,通篇的代码并不利于阅读,只贴出来一个CONSTANT_Utf8
的解析方法:
public class CONSTANT_Utf8_info extends CPInfo {
// private int length;
private String text;
public CONSTANT_Utf8_info(ClassReadCursor cusor) throws IOException {
super(ConstantPoolType.CONSTANT_Utf8);
text=cusor.readUTF();
}
public String getText() {
return text;
}
}
(本文完)