文章目录

每一个Java类在经过编译后,都会生成.class文件,作为JVM执行的输入。.class文件的结构分析和示例可以参考123。本文用Java代码分析.class文件的结构,获取类的定义,以及类中每一个域和方法的声明。

首先,.class文件是Java的字节码文件,需要逐个逐个字节的读入,因此需要一个读入文件的类,我对DataInputStream做一些简单封装,得到读入.class文件的类,如下,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package problem1;
import java.io.DataInputStream;
import java.io.IOException;
/* Class file reader wrapper
* Simple wrapper for DataInputStream
*
* u4 and u8 has problem when reading float and double
* but we do not use the value info of them
*/
public class ClassReader {
DataInputStream input;
public ClassReader(DataInputStream input) {
super();
this.input = input;
}
// read 1 byte
public byte u1() throws IOException {
return input.readByte();
}
// read 2 byte as short
public Short u2() throws IOException {
return input.readShort();
}
// read 4 byte as int, cannot read float
public int u4() throws IOException {
return input.readInt();
}
// read 8 byte as long, cannot read double
public long u8() throws IOException {
return input.readLong();
}
// wrapper for DataInputStream.readByte
public byte readByte() throws IOException{
return input.readByte();
}
// wrapper for DataInputStream.readFully
public void readFully(byte[] b) throws IOException {
input.readFully(b);
}
// wrapper for DataInputStream.close
public void close() throws IOException{
input.close();
}
}

有了这个类,就可以使用int magic = input.u4();类似这样的代码来读入.class文件中4个byte的内容,并转换为int。还需要表示常量池内元素的类和常量池类,由于常量池内的元素种类比较多,所以对不同的种类要有不同的类来表示。所以,设计常量种类的枚举类型EnumConstType,如下,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package problem1;
/* constant elem type
* tested under jdk 1.6.0_45
*
CONSTANT_Utf8 1 UTF-8编码的Unicode字符串
CONSTANT_Integer 3 int类型的字面值
CONSTANT_Float 4 float类型的字面值
CONSTANT_Long 5 long类型的字面值
CONSTANT_Double 6 double类型的字面值
CONSTANT_Class 7 对一个类或接口的符号引用
CONSTANT_String 8 String类型字面值的引用
CONSTANT_Fieldref 9 对一个字段的符号引用
CONSTANT_Methodref 10 对一个类中方法的符号引用
CONSTANT_InterfaceMethodref 11 对一个接口中方法的符号引用
CONSTANT_NameAndType 12 对一个字段或方法的部分符号引用
*/
public enum EnumConstType {
// Enumerate each constant type for jdk 1.6
C_UTF8, //(1),
C_INTEGER, //(3),
C_FLOAT, //(4),
C_LONG, //(5),
C_DOUBLE, //(6),
C_CLASS, //(7),
C_STRING, //(8),
C_FIELDREF,//(9),
C_METHODREF,//(10),
C_INTERFACEMETHODREF,//(11),
C_NAMEANDTYPE;//(12);
/* generate EnumConstType from int read from class file
*/
public static EnumConstType generateByValue(int value){
switch (value){
case 1: return EnumConstType.valueOf("C_UTF8");
case 3: return EnumConstType.valueOf("C_INTEGER");
case 4: return EnumConstType.valueOf("C_FLOAT");
case 5: return EnumConstType.valueOf("C_LONG");
case 6: return EnumConstType.valueOf("C_DOUBLE");
case 7: return EnumConstType.valueOf("C_CLASS");
case 8: return EnumConstType.valueOf("C_STRING");
case 9: return EnumConstType.valueOf("C_FIELDREF");
case 10: return EnumConstType.valueOf("C_METHODREF");
case 11: return EnumConstType.valueOf("C_INTERFACEMETHODREF");
case 12: return EnumConstType.valueOf("C_NAMEANDTYPE");
default: return null;
}
}
}

此外,常量池内元素通用的元素都包括表示常量种类的tag,和常量在常量池中的索引编号index,因此,提取这两个域设计常量元素的超类ConstElem,如下,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package problem1.constelem;
import problem1.EnumConstType;
/* super class for constant elem
* all constant elem class should extends this class
*/
abstract public class ConstElem {
EnumConstType tag; // constant type
int index; // index in constant pool
public ConstElem(EnumConstType tag, int index) {
super();
this.tag = tag;
this.index = index;
}
public EnumConstType getTag() {
return tag;
}
public void setTag(EnumConstType tag) {
this.tag = tag;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
}

对于jdk1.6的情况,可以将常量池元素的类别,EnumConstType中的不同类别结构,分成5类。类别1:CONSTANT_Utf8,包含1个字符串;类别2:CONSTANT_Integer, CONSTANT_Float,包含1个4 byte的数据;类别3:CONSTANT_Long, CONSTANT_Double,包含1个8 byte的数据;类别4:CONSTANT_Class, CONSTANT_String,包含1个2 byte的数据;类别5:CONSTANT_Fieldref, CONSTANT_Methodref, CONSTANT_NameAndType, CONSTANT_InterfaceMethodref,包含2个2 byte的数据。如果是jdk之后的版本,如jdk1.7,还包括MethodHandle, MethodType, InvokeDynamic, 分别取值为15, 16, 18的常量类别,不过在此不做考虑。根据以上5个类别设计的类,分别如下,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package problem1.constelem;
import problem1.EnumConstType;
/* Constant Element for UTF8
*/
public class ConstElemU extends ConstElem {
/* the text content for utf8 constant
*/
private String utf8;
public ConstElemU(EnumConstType tag, int index, String utf8) {
super(tag, index);
this.utf8 = utf8;
}
public String getUtf8() {
return utf8;
}
public void setUtf8(String utf8) {
this.utf8 = utf8;
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package problem1.constelem;
import problem1.EnumConstType;
/* Constant Element for int and float
* (not used)
*/
public class ConstElemIF extends ConstElem {
private int vint;
public ConstElemIF(EnumConstType tag, int index, int vint) {
super(tag, index);
this.vint = vint;
}
public int getVint() {
return vint;
}
public void setVint(int vint) {
this.vint = vint;
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package problem1.constelem;
import problem1.EnumConstType;
/* Constant Element for long and double
* (not used)
*/
public class ConstElemLD extends ConstElem {
private long vlong;
public ConstElemLD(EnumConstType tag, int index, long vlong) {
super(tag, index);
this.vlong = vlong;
}
public long getVlong() {
return vlong;
}
public void setVlong(long vlong) {
this.vlong = vlong;
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package problem1.constelem;
import problem1.EnumConstType;
/* Constant Element for class and string
*/
public class ConstElemCS extends ConstElem {
/* for Class, point to class name
* for String, point to string content
*/
short name;
public ConstElemCS(EnumConstType tag, int index, short name) {
super(tag, index);
this.name = name;
}
public short getName() {
return name;
}
public void setName(short name) {
this.name = name;
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package problem1.constelem;
import problem1.EnumConstType;
/* Constant Element for Fieldref, Methodref,
* InterfaceMethodref, and NameandType
*/
public class ConstElemFMIN extends ConstElem {
/* for Fieldref, Methodref, InterfaceMethodref, point to class info
* for NameandType, point to name
*/
short index1;
/* for Fieldref, Methodref, InterfaceMethodref, point to NameandType info
* for NameandType, point to descriptor
*/
short index2;
public ConstElemFMIN(EnumConstType tag, int index, short index1, short index2) {
super(tag, index);
this.index1 = index1;
this.index2 = index2;
}
public short getIndex1() {
return index1;
}
public void setIndex1(short index1) {
this.index1 = index1;
}
public short getIndex2() {
return index2;
}
public void setIndex2(short index2) {
this.index2 = index2;
}
}

由于,本例中没有使用到float和double值的操作,所以读取这部分的数据只是简单的存成整型,实际上,值是有错误的,不过并不影响最后的结果。有了以上这些基础类了之后,就可以在这些基础上,完成常量池类的构建,如下,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
package problem1;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import problem1.constelem.*;
/* Constant Pools
* Use a List to store Constant Pool data in class file
*/
public class ConstantPool {
// a list to keep each constant elem
public List<ConstElem> constPool = new ArrayList<ConstElem>(1024);
/* append an element in constant pool
*/
public boolean add(ConstElem a){
return constPool.add(a);
}
/* get element for ConstElem.index = index
* paras: index, index in class file, starts from 1
*/
public ConstElem getElemAt(int index){
ConstElem e = constPool.get(index-1);
if (e.getIndex() == index){
return e;
}
for (ConstElem e1:constPool){
if(e1.getIndex() == index){
return e1;
}
}
return null;
}
/* read an elem from constant pool
* paras: input, ClassReader wrapper for class file
* index, index of elem in constant pool, starts from 1
* return: type of elem
*/
public EnumConstType analyzeConstant(ClassReader input, int index)
throws IOException {
short n16, n16_1;
int n32;
long n64;
float f;
double d;
byte[] buffer;
byte tag = input.readByte(); // read constant tag
EnumConstType ctag = EnumConstType.generateByValue((int)tag);
switch (ctag) {
case C_UTF8: // utf-8 string
n16 = input.u2();
buffer = new byte[n16];
input.readFully(buffer); // read until buffer is full
constPool.add(new ConstElemU(ctag, index, new String(buffer)));
break;
case C_INTEGER: // integer
n32 = input.u4();
constPool.add(new ConstElemIF(ctag, index, n32));
break;
case C_FLOAT: // float
f = input.u4();
constPool.add(new ConstElemIF(ctag, index, (int)f));
break;
case C_LONG: // long
n64 = input.u8();
constPool.add(new ConstElemLD(ctag, index, n64));
break;
case C_DOUBLE: // double
d = input.u8();
constPool.add(new ConstElemLD(ctag, index, (long)d));
break;
case C_CLASS: // class or interface reference
n16 = input.u2();
constPool.add(new ConstElemCS(ctag, index, n16));
break;
case C_STRING: // string
n16 = input.u2();
constPool.add(new ConstElemCS(ctag, index, n16));
break;
case C_FIELDREF: // field reference
n16 = input.u2();
n16_1 = input.u2();
constPool.add(new ConstElemFMIN(ctag, index, n16, n16_1));
break;
case C_METHODREF: // method reference
n16 = input.u2();
n16_1 = input.u2();
constPool.add(new ConstElemFMIN(ctag, index, n16, n16_1));
break;
case C_INTERFACEMETHODREF: // interface method reference
n16 = input.u2();
n16_1 = input.u2();
constPool.add(new ConstElemFMIN(ctag, index, n16, n16_1));
break;
case C_NAMEANDTYPE: // name and type reference
n16 = input.u2();
n16_1 = input.u2();
constPool.add(new ConstElemFMIN(ctag, index, n16, n16_1));
break;
default:
throw new RuntimeException("Invalid constant pool flag: " + tag);
} // end switch
return ctag;
}
/* print the list of constant pool
*/
public void printConstPool(){
ConstElemU u1;
ConstElemIF if1;
ConstElemLD ld1;
ConstElemCS cs1;
ConstElemFMIN fmin1;
System.out.println("\n[ Constant Pool Info ]:");
for (ConstElem e:constPool){
System.out.println("constant index = " + e.getIndex() + ", tag = " + e.getTag());
switch(e.getTag()){
case C_UTF8:
u1 = (ConstElemU)e;
System.out.println(" length = " + u1.getUtf8().length() + " value = " + u1.getUtf8());
break;
case C_INTEGER: // INTENTIALLY left blank, same for following
case C_FLOAT:
if1 = (ConstElemIF)e;
System.out.println(" value = " + if1.getVint());
break;
case C_LONG:
case C_DOUBLE:
ld1 = (ConstElemLD)e;
System.out.println(" value = " + ld1.getVlong());
break;
case C_CLASS:
case C_STRING:
cs1 = (ConstElemCS)e;
System.out.println(" name at " + cs1.getName());
break;
case C_FIELDREF:
case C_METHODREF:
case C_INTERFACEMETHODREF:
fmin1 = (ConstElemFMIN)e;
System.out.println(" classindex = " + fmin1.getIndex1()
+ " nameandtype = " + fmin1.getIndex2());
break;
case C_NAMEANDTYPE:
fmin1 = (ConstElemFMIN)e;
System.out.println(" nameindex = " + fmin1.getIndex1()
+ " descriptor = " + fmin1.getIndex2());
break;
default:
;
}
}
}
}

对每一个读入的常量,调用analyzeConstant来将其添加到常量池中。有了这个类之后,在分析域和方法的时候,就可以根据域和方法中表示NameandType的索引找到它们的名称和类型。为了能方便的获取到这些名称和类型,设计一个工具类,来产生这些名称和类型的字符串表示,如下,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
package problem1;
import problem1.constelem.ConstElemCS;
import problem1.constelem.ConstElemU;
/* Analyze utilities
* to get the string representation of
* class, fields, methods, and access flags for them
*/
public class AnalyzeUtil {
/* get name of Class, super class, and interfaces
*/
public static String getClassName(ConstantPool cp, int index){
try{
ConstElemCS e = (ConstElemCS)cp.getElemAt(index);
ConstElemU u = (ConstElemU)cp.getElemAt(e.getName());
return u.getUtf8();
}catch(Exception e){
e.printStackTrace();
}
return null;
}
/* get normal representation of a method
*/
public static String getMethodString(ConstantPool cp, int idx, int idesc){
ConstElemU uName = (ConstElemU)cp.getElemAt(idx);
ConstElemU uDesc = (ConstElemU)cp.getElemAt(idesc);
return analyzeMethodRet(uDesc.getUtf8()) + " "
+ uName.getUtf8()
+ analyzeMethodParas(uDesc.getUtf8());
}
/* get normal representation of a single type
*/
private static String analyzeType(String t){
switch(t.charAt(0)){
case 'B': return "byte";
case 'C': return "char";
case 'D': return "double";
case 'F': return "float";
case 'I': return "int";
case 'J': return "long";
case 'S': return "short";
case 'Z': return "boolean";
case 'V': return "void";
case 'L': return t.substring(1, t.indexOf(';', 1));
case '[': return analyzeType(t.substring(1)) + "[]";
default: return null;
}
}
/* get normal representation of a method's return type
*/
private static String analyzeMethodRet(String t){
String ret = t.substring(t.indexOf(')')+1);
return analyzeType(ret);
}
/* get normal representation of a method's parameter type
*/
private static String analyzeMethodParas(String t){
String paras = t.substring(t.indexOf('(')+1, t.indexOf(')'));
String s = "";
while (!paras.isEmpty()){
int i = 0;
while (i<paras.length()){
if (paras.charAt(i) == '[') {
++i;
continue;
}
if (paras.charAt(i) == 'L') {
String para1 = paras.substring(0, paras.indexOf(';')+1);
s = s + analyzeType(para1) + ", ";
paras = paras.substring(paras.indexOf(';')+1);
break;
}
s = s + analyzeType(paras.substring(0, i+1)) +", ";
paras = paras.substring(1);
break;
}
}
if (s != ""){
s = s.substring(0, s.length()-2);
}
return "(" + s + ")";
}
/* get name and description of field
*/
public static String getFieldString(ConstantPool cp, int in, int id){
try{
ConstElemU uName = (ConstElemU)cp.getElemAt(in);
ConstElemU uDesc = (ConstElemU)cp.getElemAt(id);
return analyzeType(uDesc.getUtf8())+" "+uName.getUtf8();
}catch(Exception e){
e.printStackTrace();
}
return null;
}
/* get the String representation of class access flags
*
*/
public static String analyzeClassAF(short access_flags){
int[] access = { 0x0001, 0x0010, 0x0020, 0x0200,
0x0400, 0x1000, 0x2000, 0x4000};
String[] access_str = { "public", "final", "super", "interface",
"abstract", "synthetic", "annotation", "enum" };
return analyzeAF(access_flags, access, access_str);
}
/* get the String representation of field access flags
*
*/
public static String analyzeFieldAF(short access_flags){
int[] access = { 0x0001, 0x0002, 0x0004, 0x0008,
0x0010, 0x0040, 0x0080,
0x1000, 0x4000};
String[] access_str = { "public", "private", "protected", "static",
"final", "volatile", "transient",
"synthetic", "enum" };
return analyzeAF(access_flags, access, access_str);
}
/* get the String representation of method access flags
*
*/
public static String analyzeMethodAF(short access_flags){
int[] access = { 0x0001, 0x0002, 0x0004, 0x0008,
0x0010, 0x0020, 0x0040, 0x0080,
0x0100, 0x0400, 0x0800,
0x1000};
String[] access_str = { "public", "private", "protected", "static",
"final", "synchronized", "bridge", "varargs",
"native", "abstract", "strctfp",
"synthetic" };
return analyzeAF(access_flags, access, access_str);
}
/* get the String representation of access flags
* general form of access flags for class, fields, and methods
*/
private static String analyzeAF(short access_flags, int[] access,
String[] access_str){
String access_tmp = "";
for (int i = 0; i < access.length; i++) {
if ((access_flags & access[i]) == access[i]) {
if (i == 0)
access_tmp += access_str[i];
else if (i != 2)
access_tmp += " " + access_str[i];
}
}
return access_tmp;
}
}

以上代码,多数都是解析字符串并转换为可以理解的Java代码的形式、其中,public的方法,如getXXXX方法,可以获取类名称,以及域和方法的字符串表示;analyzeXXXXAF方法,可以获取类,域,方法的访问权限。private的方法,如analyzeXXXX方法,都是公有方法中调用到的其他方法。有了以上这些代码之后,分析class文件就很简单了,如下,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
package problem1;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.IOException;
public class ClassAnalyzer {
public static ConstantPool constPool = new ConstantPool();
public static String dir = "bin/problem1/constelem/";
public static String myClass = dir + "ConstElemU.class";
public static void main(String[] args) {
ClassReader input = null;
try {
// open class file
input = new ClassReader(
new DataInputStream(new BufferedInputStream(
new FileInputStream(myClass))));
// analyze class file
analyze(input);
} catch (Exception e) {
System.out.println("fail to analyze!");
e.printStackTrace();
} finally {
try {
input.close();
//dataout.close();
} catch (Exception e) {
}
}
}
public static void printLog(String str){
System.out.println(str);
}
public static void analyze(ClassReader input) throws IOException {
// read magic number
int magic = input.u4();
if (magic == 0xCAFEBABE){
printLog("[ General Info ]:");
printLog("magic number = 0xCAFEBABE");
} else {
throw new RuntimeException("unknown magic number!");
}
// read major version and minor version
int minor_ver = input.u2();
int major_ver = input.u2();
printLog("Version = " + major_ver + "." + minor_ver);
// read number of constant
short const_pool_count = input.u2();
printLog("constant pool size = " + const_pool_count);
// read each constant in constant pool
EnumConstType tp = null;
for (int i = 1; i < const_pool_count; ++i){
tp = constPool.analyzeConstant(input, i); // analyze each constant
// for long and double, they use 2 constant index
if (tp == EnumConstType.C_LONG || tp == EnumConstType.C_DOUBLE){
++i;
}
}
// print Constant Pool when needed
constPool.printConstPool();
// read access flags for class
short access_flags = input.u2();
// read class, and super class
short this_class_index = input.u2();
short super_class_index = input.u2();
// print class, super class, and interface
// print access flags for this class
printLog("\n[ Class Info ]:");
printLog("This class index = " + this_class_index +", name = "
+ AnalyzeUtil.getClassName(constPool, this_class_index));
System.out.print(" access_flags = " + String.format("0x%04x", access_flags));
String access_tmp = AnalyzeUtil.analyzeClassAF(access_flags);
printLog(" [ " + access_tmp + " ]");
printLog("Super class index = " + super_class_index +", name = "
+ AnalyzeUtil.getClassName(constPool, super_class_index));
// read interfaces count:
short inteCes_count = input.u2();
printLog("\n[ Interface Info ]:");
printLog("number of interface = " + inteCes_count);
// read each interface:
for (int i = 1; i <= inteCes_count; i++) {
short inteCe_index = input.u2();
printLog("No. " + i + " interface index = " + inteCe_index + ", name = "
+ AnalyzeUtil.getClassName(constPool, inteCe_index));
}
// read field count:
short field_count = input.u2();
printLog("\n[ Field Info ]:");
printLog("number of field = " + field_count);
// read each field:
for (int i = 1; i <= field_count; i++) {
short field_access_flag = input.u2();
short field_name_index = input.u2();
short field_descriptor_index = input.u2();
short field_attributes_count = input.u2();
for (int j=1; j<=field_attributes_count; j++){
short attribute_name_index = input.u2();
int attribute_length = input.u4();
for (int k=1; k<=attribute_length; k++){
input.u1(); // not used attributes
}
}
printLog("No. " + i + " access_flag = "
+ String.format("0x%04x", field_access_flag)+ " name_index = "
+ field_name_index+ " descriptor_index = "
+ field_descriptor_index);
printLog(" Original Form: "
+ AnalyzeUtil.analyzeFieldAF(field_access_flag) + " "
+ AnalyzeUtil.getFieldString(constPool, field_name_index,
field_descriptor_index) + ";");
}
// read method count:
short method_count = input.u2();
printLog("\n[ Method Info ]:");
printLog("number of method = " + method_count);
// read each method:
for (int i = 1; i <= method_count; i++) {
short method_access_flag = input.u2();
short method_name_index = input.u2();
short method_descriptor_index = input.u2();
short method_attributes_count = input.u2();
for (int j=1; j<=method_attributes_count; j++){
short attribute_name_index = input.u2();
int attribute_length = input.u4();
for (int k=1; k<=attribute_length; k++){
input.u1(); // not used attributes
}
}
printLog("No. " + i + " access_flag = "
+ String.format("0x%04x", method_access_flag)
+ " name_index = "
+ method_name_index+ " descriptor_index = "
+ method_descriptor_index);
printLog(" Original Form: "
+ AnalyzeUtil.analyzeMethodAF(method_access_flag) + " "
+ AnalyzeUtil.getMethodString(constPool, method_name_index,
method_descriptor_index) + ";");
}
}
}

以上代码的main入口读取class文件,并调用analyze来分析。analyze大致分两个步骤,到printConstPool之前,这一段主要是读取常量池的内容,构造常量池对象constPool,有一个小细节是,如果类型是long或者double,那么需要占用2个索引值index,其他情况都是占用1个;之后的部分,是读取类、域、方法的信息,然后从常量池中找到它们的索引值,并输出在屏幕上,输出的时候调用AnalyzeUtil中的方法,以便可以获取易于理解的输出。本例用的是之前的ConstElemU作为测试class,输入如下,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
[ General Info ]:
magic number = 0xCAFEBABE
Version = 50.0
constant pool size = 29
[ Constant Pool Info ]:
constant index = 1, tag = C_CLASS
name at 2
constant index = 2, tag = C_UTF8
length = 29 value = problem1/constelem/ConstElemU
constant index = 3, tag = C_CLASS
name at 4
constant index = 4, tag = C_UTF8
length = 28 value = problem1/constelem/ConstElem
constant index = 5, tag = C_UTF8
length = 4 value = utf8
constant index = 6, tag = C_UTF8
length = 18 value = Ljava/lang/String;
constant index = 7, tag = C_UTF8
length = 6 value = <init>
constant index = 8, tag = C_UTF8
length = 46 value = (Lproblem1/EnumConstType;ILjava/lang/String;)V
constant index = 9, tag = C_UTF8
length = 4 value = Code
constant index = 10, tag = C_METHODREF
classindex = 3 nameandtype = 11
constant index = 11, tag = C_NAMEANDTYPE
nameindex = 7 descriptor = 12
constant index = 12, tag = C_UTF8
length = 28 value = (Lproblem1/EnumConstType;I)V
constant index = 13, tag = C_FIELDREF
classindex = 1 nameandtype = 14
constant index = 14, tag = C_NAMEANDTYPE
nameindex = 5 descriptor = 6
constant index = 15, tag = C_UTF8
length = 15 value = LineNumberTable
constant index = 16, tag = C_UTF8
length = 18 value = LocalVariableTable
constant index = 17, tag = C_UTF8
length = 4 value = this
constant index = 18, tag = C_UTF8
length = 31 value = Lproblem1/constelem/ConstElemU;
constant index = 19, tag = C_UTF8
length = 3 value = tag
constant index = 20, tag = C_UTF8
length = 24 value = Lproblem1/EnumConstType;
constant index = 21, tag = C_UTF8
length = 5 value = index
constant index = 22, tag = C_UTF8
length = 1 value = I
constant index = 23, tag = C_UTF8
length = 7 value = getUtf8
constant index = 24, tag = C_UTF8
length = 20 value = ()Ljava/lang/String;
constant index = 25, tag = C_UTF8
length = 7 value = setUtf8
constant index = 26, tag = C_UTF8
length = 21 value = (Ljava/lang/String;)V
constant index = 27, tag = C_UTF8
length = 10 value = SourceFile
constant index = 28, tag = C_UTF8
length = 15 value = ConstElemU.java
[ Class Info ]:
This class index = 1, name = problem1/constelem/ConstElemU
access_flags = 0x0021 [ public ]
Super class index = 3, name = problem1/constelem/ConstElem
[ Interface Info ]:
number of interface = 0
[ Field Info ]:
number of field = 1
No. 1 access_flag = 0x0002 name_index = 5 descriptor_index = 6
Original Form: private java/lang/String utf8;
[ Method Info ]:
number of method = 3
No. 1 access_flag = 0x0001 name_index = 7 descriptor_index = 8
Original Form: public void <init>(problem1/EnumConstType, int, java/lang/String);
No. 2 access_flag = 0x0001 name_index = 23 descriptor_index = 24
Original Form: public java/lang/String getUtf8();
No. 3 access_flag = 0x0001 name_index = 25 descriptor_index = 26
Original Form: public void setUtf8(java/lang/String);

输出的内容很多,前一段是输出整个常量池的内容,如果不看这部分,剩下的内容从[ Class Info ]:开始,就很少了。[ Class Info ]:部分,可以得知,class文件的类是problem1/constelem/ConstElemU,是public的,其超类是problem1/constelem/ConstElem[ Interface Info ]:部分没有内容,因为该类没有实现接口;[ Field Info ]:部分,有1个域,可以写成private java/lang/String utf8,也就是private String utf8[ Method Info ]:有3个方法,第1个是构造方法public void <init>(problem1/EnumConstType, int, java/lang/String),构造方法的名称统一写成了<init>,需要替换为类名,并加了void返回值,实际不需要,略微改写为public ConstElemU(EnumConstType, int, String),另外2个方法public java/lang/String getUtf8()public void setUtf8(java/lang/String),可以改写为public String getUtf8()public void setUtf8(String)。对比原本的代码可以发现,以上输出,基本还原了ConstElemU的类定义。

本例的代码还可以进一步修改来支持float和double类型常数,代码参考了4

文章目录

欢迎来到Valleylord的博客!

本博的文章尽量原创。