解析ConstantValue属性
解析ConstantValue属性
ConstantValue属性用于通知虚拟机在类或接口初始化阶段为被标志为ACC_STATIC的字段自动赋值,如接口中声明的字段,类中声明的静态常量字段。其它非ACC_STATIC的字段是在类的实例初始化方法中完成的。
在字段结构的属性表中最多只能有一个ConstantValue属性。字段的类型必须是基本数据类型或者String类,因为从常量池中只能引用到基本类型和String类型的字面量。ConstantValue属性的结构如下:
public class ConstantValue_attribute { private U2 attribute_name_index; private U4 attribute_length; private U2 constantvalue_index; }
由于ConstantValue属性是定长属性,因此attribute_length的值固定为2。attribute_name_index指向常量池中某个CONSTANT_Utf8_info常量,该常量表示的字符串为“ConstantValue”。因为属性表的属性结构并不像常量池的常量结构那样,有一个tag字段映射到哪种常量结构,因此,只能通过attribute_name_index属性判断是哪种属性。constantvalue_index指向基本数据类型或String类型常量。
以一个例子来说明ConstantValue属性的使用场景。在一个接口中定义一个字段并赋值,通过分析其Class文件结构,找到这个字段的属性表,看是否有ConstantValue属性。该接口如代码清单清单2-57所示。
代码清单2-57 在接口中定义字段
public interface TestConstantValueInterface { int value = 1000; }
现在,我们需要编写一个属性解析工具类,添加支持解析ConstantValue属性的静态方法,以完成属性的二次解析工作。如代码清单2-58所示。
代码清单2-58 ConstantValue属性解析方法
public class AttributeProcessingFactory { public static ConstantValue_attribute processingConstantValue(AttributeInfo attributeInfo) { ConstantValue_attribute attribute = new ConstantValue_attribute(); attribute.setAttribute_name_index(attributeInfo.getAttribute_name_index()); attribute.setAttribute_length(attributeInfo.getAttribute_length()); attribute.setConstantvalue_index(new U2(attributeInfo.getInfo()[0], attributeInfo.getInfo()[1])); return attribute; } }
编写单元测试,解析TestConstantValueInterface接口编译后的class文件,获取该接口的所有字段信息,然后遍历字段,拿到每个字段的属性表,接着遍历属性表,根据属性的名称在常量池中的索引到常量池中取到属性名,最后根据属性名去调用对应的属性结构的解析方法对其进行二次解析。
因为当前只实现了ConstantValue属性的解析,所以单元测试中只对名称为“ConstantValue”的属性进行二次解析。如代码清单2-59所示。
代码清单2-59 ConstantValue属性解析单元测试
public class ConstantValueAttributeTest { @Test public void testConstantValue() throws Exception { ByteBuffer codeBuf = ClassFileAnalysisMain.readFile(".../TestConstantValueInterface.class"); ClassFile classFile = ClassFileAnalysiser.analysis(codeBuf); // 获取所有字段 FieldInfo[] fieldInfos = classFile.getFields(); for (FieldInfo fieldInfo : fieldInfos) { // 获取字段的所有属性 AttributeInfo[] attributeInfos = fieldInfo.getAttributes(); if (attributeInfos == null || attributeInfos.length == 0) { continue; } System.out.println("字段:" + classFile.getConstant_pool() [fieldInfo.getName_index().toInt() - 1]); // 遍历所有属性 for (AttributeInfo attributeInfo : attributeInfos) { // 获取属性的名称 U2 name_index = attributeInfo.getAttribute_name_index(); CONSTANT_Utf8_info name_info = (CONSTANT_Utf8_info) classFile.getConstant_pool()[name_index.toInt() - 1]; String name = new String(name_info.getBytes()); // 如果属性名是ConstantValue,则对该属性进行二次解析 if (name.equalsIgnoreCase("ConstantValue")) { // 属性二次解析 ConstantValue_attribute constantValue = AttributeProcessingFactory.processingConstantValue(attributeInfo); // 取得constantvalue_index,从常量池中取值 U2 cv_index = constantValue.getConstantvalue_index(); Object cv = classFile.getConstant_pool() [cv_index.toInt() - 1]; // 需要判断常量的类型 if (cv instanceof CONSTANT_Utf8_info) { System.out.println("ConstantValue:" + cv.toString()); } else if (cv instanceof CONSTANT_Integer_info) { System.out.println("ConstantValue:" + ((CONSTANT_Integer_info) cv).getBytes().toInt()); } else if (cv instanceof CONSTANT_Float_info) { // todo } else if (cv instanceof CONSTANT_Long_info) { // todo } else if (cv instanceof CONSTANT_Double_info) { // todo } } } } } }
单元测试结果输出如图2-12所示。

图2-12 ConstantValue属性解析单元测试
解析结果如图2-12所示,字段名为value的字段,其属性表有一个ConstantValue属性,常量值是1000。