官术网_书友最值得收藏!

解析方法表

方法表存放一個類或者接口的所有方法。與字段結(jié)構(gòu)一樣,方法結(jié)構(gòu)也有屬性表,方法編譯后的字節(jié)碼指令是存放在方法結(jié)構(gòu)的屬性表中的,對應(yīng)Code屬性。但不是所有方法都會有Code屬性,如接口中的方法不一定會有Code屬性,如抽象方法一定沒有Code屬性。方法包括靜態(tài)方法、以及類的初始化方法<clinit>和類的實(shí)例初始化方法<init>。參照《Java虛擬機(jī)規(guī)范》,方法結(jié)構(gòu)如表2-49所示。

表2-49 方法結(jié)構(gòu)

其中方法名稱索引、方法描述符索引與字段結(jié)構(gòu)中的字段名索引和字段類型描述符索引,都是指向常量池中某個CONSTABT_Utf8_info結(jié)構(gòu)的常量,屬性總數(shù)與屬性表也與字段結(jié)構(gòu)中的一樣,但不同的是,屬性的結(jié)構(gòu)不同,如方法有Code屬性而字段沒有。訪問標(biāo)志也與字段的訪問標(biāo)志有些區(qū)別,如字段有ACC_VOLATILE標(biāo)志而方法沒有。關(guān)于方法的訪問權(quán)限及屬性標(biāo)志如表2-50所示。

表2-50 方法訪問權(quán)限及屬性標(biāo)志映射表

根據(jù)表2-49方法結(jié)構(gòu)創(chuàng)建對應(yīng)的Java類MethodInfo,如代碼清單2-51所示。

代碼清單2-51 MethodInfo類

public class MethodInfo {

    private U2 access_flags;
    private U2 name_index;
    private U2 descriptor_index;
    private U2 attributes_count;
    private AttributeInfo[] attributes;

}

與字段表的解析流程一樣,我們暫時不關(guān)心屬性表的具體屬性的解析,屬性表的解析只使用通用屬性結(jié)構(gòu)解析。方法表解析器的實(shí)現(xiàn)如代碼清單2-52所示。

代碼清單2-52 方法表解析器

public class MethodHandler implements BaseByteCodeHandler {

    @Override
    public int order() {
        // 排在字段解析器的后面
        return 7;
    }

    @Override
    public void read(ByteBuffer codeBuf, ClassFile classFile)
                        throws Exception {
       classFile.setMethods_count(new U2(codeBuf.get(), codeBuf.get()));
        // 獲取方法總數(shù)
        int len = classFile.getMethods_count().toInt();
        if (len == 0) {
            return;
        }
        // 創(chuàng)建方法表
        MethodInfo[] methodInfos = new MethodInfo[len];
        classFile.setMethods(methodInfos);
        for (int i = 0; i < methodInfos.length; i++) {
            // 解析方法
            methodInfos[i] = new MethodInfo();
            methodInfos[i].setAccess_flags(new U2(codeBuf.get(),codeBuf.get()));
            methodInfos[i].setName_index(new U2(codeBuf.get(), codeBuf.get()));
            methodInfos[i].setDescriptor_index(new U2(codeBuf.get(), codeBuf.get()));
            methodInfos[i].setAttributes_count(new U2(codeBuf.get(), codeBuf.get()));
            // 獲取方法的屬性總數(shù)
            int attr_len = methodInfos[i].getAttributes_count().toInt();
            if (attr_len == 0) {
                continue;
            }
            // 創(chuàng)建方法的屬性表
            methodInfos[i].setAttributes(new AttributeInfo[attr_len]);
            for (int j = 0; j < attr_len; j++) {
                methodInfos[i].getAttributes()[j] = new AttributeInfo();
                // 解析方法的屬性
                methodInfos[i].getAttributes()[j]
                        .setAttribute_name_index(new U2(codeBuf.get(), codeBuf.get()));
                // 獲取屬性info的長度
                U4 attr_info_len = new U4(codeBuf.get(), codeBuf.get(), codeBuf.get(), codeBuf.get());
                methodInfos[i].getAttributes()[j] .setAttribute_length(attr_info_len);
                if (attr_info_len.toInt() == 0) {
                    continue;
                }
                // 解析info
                byte[] info = new byte[attr_info_len.toInt()];
                codeBuf.get(info, 0, attr_info_len.toInt());
                methodInfos[i].getAttributes()[j].setInfo(info);
            }
        }
    }

}

將方法表解析器注冊到ClassFileAnalysiser后,我們來編寫單元測試。方法表解析器的單元測試與字段表解析器的單元測試邏輯差不多,如代碼清單2-53所示。

代碼清單2-53 方法表解析器單元測試

public class MethodHandlerTest {

    private static String getName(U2 name_index, ClassFile classFile) {
        CONSTANT_Utf8_info name_info = (CONSTANT_Utf8_info)
                      classFile.getConstant_pool()[name_index.toInt() - 1];
        return name_info.toString();
    }

    @Test
    public void testMethodHandlerHandler() throws Exception {
        ByteBuffer codeBuf = ClassFileAnalysisMain.readFile("Builder.class");
        ClassFile classFile = ClassFileAnalysiser.analysis(codeBuf);
        System.out.println("方法總數(shù):" + classFile.getMethods_count().toInt());
        System.out.println();
        MethodInfo[] methodInfos = classFile.getMethods();
    // 遍歷方法表
        for (MethodInfo methodInfo : methodInfos) {
            System.out.println("訪問標(biāo)志和屬性:" +   FieldAccessFlagUtils
.toFieldAccessFlagsString(methodInfo.getAccess_flags()));
            System.out.println("方法名:" + getName(methodInfo.getName_index(), classFile));
            System.out.println("方法描述符:"
             + getName(methodInfo.getDescriptor_index(), classFile));
            System.out.println("屬性總數(shù):" + methodInfo.getAttributes_count().toInt());
            System.out.println();
        }
    }

}

單元測試結(jié)果輸出如圖2.10所示。

圖2.10 方法表解析器單元測試

從輸出的結(jié)果可以看出,該單元測試解析的class文件,有5個方法,訪問權(quán)限都是public,其中有一個方法是靜態(tài)方法;這五個方法的屬性表都只有一個屬性,實(shí)際都是Code屬性;這五個方法的方法名稱分別是<init>、setA、setB、setC和main;還能看到各個方法的方法描述符。

主站蜘蛛池模板: 永泰县| 秦皇岛市| 敦化市| 涪陵区| 河间市| 永福县| 林甸县| 宜城市| 宁河县| 屏南县| 射洪县| 牟定县| 历史| 庐江县| 岳阳县| 清镇市| 辽中县| 太和县| 什邡市| 耒阳市| 湄潭县| 岐山县| 隆德县| 那坡县| 晴隆县| 平度市| 正蓝旗| 福海县| 信宜市| 响水县| 古交市| 安化县| 襄城县| 修文县| 丽水市| 红桥区| 确山县| 太原市| 永清县| 汤阴县| 苏尼特右旗|