文章目录
每一个Java类在经过编译后,都会生成.class文件,作为JVM执行的输入。.class文件的结构分析和示例可以参考,,。本文用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;
* 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;
}
public byte u1 () throws IOException {
return input.readByte();
}
public Short u2 () throws IOException {
return input.readShort();
}
public int u4 () throws IOException {
return input.readInt();
}
public long u8 () throws IOException {
return input.readLong();
}
public byte readByte () throws IOException{
return input.readByte();
}
public void readFully (byte [] b) throws IOException {
input.readFully(b);
}
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;
* 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 {
C_UTF8,
C_INTEGER,
C_FLOAT,
C_LONG,
C_DOUBLE,
C_CLASS,
C_STRING,
C_FIELDREF,
C_METHODREF,
C_INTERFACEMETHODREF,
C_NAMEANDTYPE;
*/
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;
* all constant elem class should extends this class
*/
abstract public class ConstElem {
EnumConstType tag;
int index;
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;
*/
public class ConstElemU extends ConstElem {
*/
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;
* (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;
* (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;
*/
public class ConstElemCS extends ConstElem {
* 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;
* InterfaceMethodref, and NameandType
*/
public class ConstElemFMIN extends ConstElem {
* for NameandType, point to name
*/
short index1;
* 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.*;
* Use a List to store Constant Pool data in class file
*/
public class ConstantPool {
public List<ConstElem> constPool = new ArrayList<ConstElem>(1024 );
*/
public boolean add (ConstElem a){
return constPool.add(a);
}
* 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 ;
}
* 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();
EnumConstType ctag = EnumConstType.generateByValue((int )tag);
switch (ctag) {
case C_UTF8:
n16 = input.u2();
buffer = new byte [n16];
input.readFully(buffer);
constPool.add(new ConstElemU(ctag, index, new String(buffer)));
break ;
case C_INTEGER:
n32 = input.u4();
constPool.add(new ConstElemIF(ctag, index, n32));
break ;
case C_FLOAT:
f = input.u4();
constPool.add(new ConstElemIF(ctag, index, (int )f));
break ;
case C_LONG:
n64 = input.u8();
constPool.add(new ConstElemLD(ctag, index, n64));
break ;
case C_DOUBLE:
d = input.u8();
constPool.add(new ConstElemLD(ctag, index, (long )d));
break ;
case C_CLASS:
n16 = input.u2();
constPool.add(new ConstElemCS(ctag, index, n16));
break ;
case C_STRING:
n16 = input.u2();
constPool.add(new ConstElemCS(ctag, index, n16));
break ;
case C_FIELDREF:
n16 = input.u2();
n16_1 = input.u2();
constPool.add(new ConstElemFMIN(ctag, index, n16, n16_1));
break ;
case C_METHODREF:
n16 = input.u2();
n16_1 = input.u2();
constPool.add(new ConstElemFMIN(ctag, index, n16, n16_1));
break ;
case C_INTERFACEMETHODREF:
n16 = input.u2();
n16_1 = input.u2();
constPool.add(new ConstElemFMIN(ctag, index, n16, n16_1));
break ;
case C_NAMEANDTYPE:
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);
}
return ctag;
}
*/
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:
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;
* to get the string representation of
* class, fields, methods, and access flags for them
*/
public class AnalyzeUtil {
*/
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 ;
}
*/
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());
}
*/
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 ;
}
}
*/
private static String analyzeMethodRet (String t){
String ret = t.substring(t.indexOf(')' )+1 );
return analyzeType(ret);
}
*/
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 + ")" ;
}
*/
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 ;
}
*
*/
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);
}
*
*/
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);
}
*
*/
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);
}
* 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 {
input = new ClassReader(
new DataInputStream(new BufferedInputStream(
new FileInputStream(myClass))));
analyze(input);
} catch (Exception e) {
System.out.println("fail to analyze!" );
e.printStackTrace();
} finally {
try {
input.close();
} catch (Exception e) {
}
}
}
public static void printLog (String str){
System.out.println(str);
}
public static void analyze (ClassReader input) throws IOException {
int magic = input.u4();
if (magic == 0xCAFEBABE ){
printLog("[ General Info ]:" );
printLog("magic number = 0xCAFEBABE" );
} else {
throw new RuntimeException("unknown magic number!" );
}
int minor_ver = input.u2();
int major_ver = input.u2();
printLog("Version = " + major_ver + "." + minor_ver);
short const_pool_count = input.u2();
printLog("constant pool size = " + const_pool_count);
EnumConstType tp = null ;
for (int i = 1 ; i < const_pool_count; ++i){
tp = constPool.analyzeConstant(input, i);
if (tp == EnumConstType.C_LONG || tp == EnumConstType.C_DOUBLE){
++i;
}
}
constPool.printConstPool();
short access_flags = input.u2();
short this_class_index = input.u2();
short super_class_index = input.u2();
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));
short inteCes_count = input.u2();
printLog("\n[ Interface Info ]:" );
printLog("number of interface = " + inteCes_count);
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));
}
short field_count = input.u2();
printLog("\n[ Field Info ]:" );
printLog("number of field = " + field_count);
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();
}
}
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) + ";" );
}
short method_count = input.u2();
printLog("\n[ Method Info ]:" );
printLog("number of method = " + method_count);
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();
}
}
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 = 0 xCAFEBABE
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 = 0 x0021 [ 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 = 0 x0002 name_index = 5 descriptor_index = 6
Original Form: private java/lang/String utf8;
[ Method Info ]:
number of method = 3
No. 1 access_flag = 0 x0001 name_index = 7 descriptor_index = 8
Original Form: public void <init>(problem1/EnumConstType, int, java/lang/String);
No. 2 access_flag = 0 x0001 name_index = 23 descriptor_index = 24
Original Form: public java/lang/String getUtf8();
No. 3 access_flag = 0 x0001 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类型常数,代码参考了。