当前位置: 首页 > 面试题库 >

我将如何解析Java类文件常量池?

谈琦
2023-03-14
问题内容

据https://en.wikipedia.org/wiki/Java_class_file#General_layout -
类文件的Java的常量池中开始的10个字节到文件中。

到目前为止,我已经能够解析之前的所有内容(可以神奇地检查它是否是
一个类文件,主要/次要版本,常量池大小),但我仍然不
知道如何准确解析常量池。像,是否有用于
指定方法引用和其他内容的操作码?

在用
十六进制表示文本之前,有什么方法可以引用每个十六进制值,以找出以下值是什么?

我应该通过用NOP(0x00)分割每组条目然后
解析不是文本值的每个字节来解决问题吗?

例如,如何确定这些值分别代表什么?


问题答案:

与类文件唯一相关的文档是 The Java®
Virtual Machine
Specification
,
especially Chapter 4. The class File
Format and,
if you are going to parse more than the constant pool, Chapter 6. The Java
Virtual Machine Instruction
Set.

常量池由长度可变的项组成,这些项的第一个字节
确定其类型,而类型又决定了大小。大多数项目由
指向其他项目的一两个索引组成。
不需要任何第三方库的简单解析代码可能如下所示:

public static final int HEAD=0xcafebabe;
// Constant pool types
public static final byte CONSTANT_Utf8               = 1;
public static final byte CONSTANT_Integer            = 3;
public static final byte CONSTANT_Float              = 4;
public static final byte CONSTANT_Long               = 5;
public static final byte CONSTANT_Double             = 6;
public static final byte CONSTANT_Class              = 7;
public static final byte CONSTANT_String             = 8;
public static final byte CONSTANT_FieldRef           = 9;
public static final byte CONSTANT_MethodRef          =10;
public static final byte CONSTANT_InterfaceMethodRef =11;
public static final byte CONSTANT_NameAndType        =12;
public static final byte CONSTANT_MethodHandle       =15;
public static final byte CONSTANT_MethodType         =16;
public static final byte CONSTANT_InvokeDynamic      =18;
public static final byte CONSTANT_Module             =19;
public static final byte CONSTANT_Package            =20;

static void parseRtClass(Class<?> clazz) throws IOException, URISyntaxException {
    URL url = clazz.getResource(clazz.getSimpleName()+".class");
    if(url==null) throw new IOException("can't access bytecode of "+clazz);
    Path p = Paths.get(url.toURI());
    if(!Files.exists(p))
        p = p.resolve("/modules").resolve(p.getRoot().relativize(p));
    parse(ByteBuffer.wrap(Files.readAllBytes(p)));
}
static void parseClassFile(Path path) throws IOException {
    ByteBuffer bb;
    try(FileChannel ch=FileChannel.open(path, StandardOpenOption.READ)) {
        bb=ch.map(FileChannel.MapMode.READ_ONLY, 0, ch.size());
    }
    parse(bb);
}
static void parse(ByteBuffer buf) {
    if(buf.order(ByteOrder.BIG_ENDIAN).getInt()!=HEAD) {
        System.out.println("not a valid class file");
        return;
    }
    int minor=buf.getChar(), ver=buf.getChar();
    System.out.println("version "+ver+'.'+minor);
    for(int ix=1, num=buf.getChar(); ix<num; ix++) {
        String s; int index1=-1, index2=-1;
        byte tag = buf.get();
        switch(tag) {
            default:
                System.out.println("unknown pool item type "+buf.get(buf.position()-1));
                return;
            case CONSTANT_Utf8: decodeString(ix, buf); continue;
            case CONSTANT_Class: case CONSTANT_String: case CONSTANT_MethodType:
            case CONSTANT_Module: case CONSTANT_Package:
                s="%d:\t%s ref=%d%n"; index1=buf.getChar();
                break;
            case CONSTANT_FieldRef: case CONSTANT_MethodRef:
            case CONSTANT_InterfaceMethodRef: case CONSTANT_NameAndType:
                s="%d:\t%s ref1=%d, ref2=%d%n";
                index1=buf.getChar(); index2=buf.getChar();
                break;
            case CONSTANT_Integer: s="%d:\t%s value="+buf.getInt()+"%n"; break;
            case CONSTANT_Float: s="%d:\t%s value="+buf.getFloat()+"%n"; break;
            case CONSTANT_Double: s="%d:\t%s value="+buf.getDouble()+"%n"; ix++; break;
            case CONSTANT_Long: s="%d:\t%s value="+buf.getLong()+"%n"; ix++; break;
            case CONSTANT_MethodHandle:
                s="%d:\t%s kind=%d, ref=%d%n"; index1=buf.get(); index2=buf.getChar();
                break;
             case CONSTANT_InvokeDynamic:
                s="%d:\t%s bootstrap_method_attr_index=%d, ref=%d%n";
                index1=buf.getChar(); index2=buf.getChar();
                break;
        }
        System.out.printf(s, ix, FMT[tag], index1, index2);
    }
}
private static String[] FMT= {
    null, "Utf8", null, "Integer", "Float", "Long", "Double", "Class",
    "String", "Field", "Method", "Interface Method", "Name and Type",
    null, null, "MethodHandle", "MethodType", null, "InvokeDynamic",
    "Module", "Package"
};

private static void decodeString(int poolIndex, ByteBuffer buf) {
    int size=buf.getChar(), oldLimit=buf.limit();
    buf.limit(buf.position()+size);
    StringBuilder sb=new StringBuilder(size+(size>>1)+16)
        .append(poolIndex).append(":\tUtf8 ");
    while(buf.hasRemaining()) {
        byte b=buf.get();
        if(b>0) sb.append((char)b);
        else
        {
            int b2 = buf.get();
            if((b&0xf0)!=0xe0)
                sb.append((char)((b&0x1F)<<6 | b2&0x3F));
            else
            {
                int b3 = buf.get();
                sb.append((char)((b&0x0F)<<12 | (b2&0x3F)<<6 | b3&0x3F));
            }
        }
    }
    buf.limit(oldLimit);
    System.out.println(sb);
}

不要被这些getChar()呼叫弄糊涂了,我将它们用作
获取未签名的缩写的便捷方法,而不是getShort()&0xffff

上面的代码仅打印其他池项目的引用索引。
为了对项目进行解码,您可以首先将所有项目的数据存储到
随机访问数据结构中,即数组或List由于项目可能引用
具有较高索引号的项目。并从索引开始1



 类似资料:
  • 我正在使用GSON解析一个JSON文件,并希望将这个JSON对象映射到一个POJO类。问题是JSON中的属性名没有camel-case,但我的java POJO对象具有camel-case属性名。 有没有任何想法没有任何表现打击? 例如:JSON文件中的属性名是'ordernumber',但在我的POJO类中,我用'Sales Number'作为属性名,而不是ordernumber。现在,我们如何

  • 问题内容: 我正在开发一个Android应用程序,而我在Java和Android上还是一个新手。 我想创建一些要在某些活动中使用的常量。在哪里可以定义这些常数? 谢谢。 问题答案: 在Java和大多数OO语言中,仅定义一个类来保存常量被认为是不好的做法。最好在与它们关联的类中定义常量。通常只有一个。例如 如果真的没有,可以随意定义一个单独的类。 编辑:这里的关键是: 使常数易于查找。如果有放置它们

  • 本文向大家介绍java如何解析/读取xml文件,包括了java如何解析/读取xml文件的使用技巧和注意事项,需要的朋友参考一下 本文实例为大家分享了java解析/读取xml文件的方法,供大家参考,具体内容如下 XML文件 Java 代码: 以上就是本文的全部内容,希望对大家的学习有所帮助。

  • 问题内容: 到目前为止,我的目标是在Rust中解析此JSON数据: 并且是 我下一步应该解析什么?我的主要目标是获取这样的JSON数据,并从其中解析密钥(例如Age)。 问题答案: Serde是首选的JSON序列化提供程序。您可以通过多种方式从文件中读取JSON文本。将其作为字符串使用后,请使用: Cargo.toml: 您甚至可以使用类似的方法直接从已打开的读取。 Serde可以用于JSON以外

  • 问题内容: 我正在尝试解析下面的文件。我想打印每个乘客的身份证和姓名。你能给我代码解析吗? 问题答案: 这是一个工作示例,尽管我建议您改用StAX,但您会发现SAX不太方便 输出

  • 问题内容: 我想在Java中解析JSON文件,并从下面提到的文件中获取以下值: 从每个元素,我想获得距离和持续时间的值字段。我该怎么做呢? 问题答案: 使用json.org参考实现(org.json主页,在此处下载)。代码有点混乱,但是我认为它可以满足您的要求。通过不创建所有这些对象而是直接访问它们,可以采用很多快捷方式。我这样做的原因是试图使其更容易跟踪发生的事情。