2

是否可以从由类的源代码形成的字节码中挖掘出类名?

情况是这样的:我从某个地方远程获取一个类字节码,它来自哪里并不重要。为了有效地使用类加载器加载该类,我还需要具有类名......对吗?

4

6 回答 6

11

如果您只需要类名,则自己解析类文件的开头可能更容易,而不是为此目的添加用于类代码操作的 3rd 方库。您只需要常量池中的类和字符串,跳过访问标志,然后将 / 替换为 . 在类名中。如果你有一个字节数组,你可以调用这个方法new ByteArrayInputStream(byteArray)

public static String getClassName(InputStream is) throws Exception {
    DataInputStream dis = new DataInputStream(is);
    dis.readLong(); // skip header and class version
    int cpcnt = (dis.readShort()&0xffff)-1;
    int[] classes = new int[cpcnt];
    String[] strings = new String[cpcnt];
    for(int i=0; i<cpcnt; i++) {
        int t = dis.read();
        if(t==7) classes[i] = dis.readShort()&0xffff;
        else if(t==1) strings[i] = dis.readUTF();
        else if(t==5 || t==6) { dis.readLong(); i++; }
        else if(t==8) dis.readShort();
        else dis.readInt();
    }
    dis.readShort(); // skip access flags
    return strings[classes[(dis.readShort()&0xffff)-1]-1].replace('/', '.');
}
于 2009-10-30T15:20:28.163 回答
2

最简单的方法可能是使用ASM之类的东西:

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.commons.EmptyVisitor;

public class PrintClassName {
  public static void main(String[] args) throws IOException {
    class ClassNamePrinter extends EmptyVisitor {
      @Override
      public void visit(int version, int access, String name, String signature,
          String superName, String[] interfaces) {
        System.out.println("Class name: " + name);
      }
    }

    InputStream binary = new FileInputStream(args[0]);
    try {
      ClassReader reader = new ClassReader(binary);
      reader.accept(new ClassNamePrinter(), 0);
    } finally {
      binary.close();
    }
  }
}

如果您不能使用 3rd 方库,您可以自己阅读类文件格式

于 2009-10-30T13:26:21.683 回答
1

如果这种情况偶尔发生,您应该能够使用javap反汇编字节码。

在运行时执行此操作:使用 Apache 的 BCEL ( http://jakarta.apache.org/bcel ) 之类的字节码操作库来分析字节码。

于 2009-10-30T13:00:31.783 回答
1

为了完整起见,在可接受使用 ASM5 库的情况下,可以使用以下调用从其字节表示中获取类名。

public String readClassName(final byte[] typeAsByte) {
    return new ClassReader(typeAsByte).getClassName().replace("/", ".");
}
于 2015-12-25T03:43:46.327 回答
0

我认为您可以在 ClassLoader 的子类中使用 ClassLoader.defineClass 方法来获取给定字节码的 Class 对象。(未测试)

于 2009-10-30T13:27:29.683 回答
0

开源库java-util具有 API ReflectionUtils.getClassNameFromByteCode(byte[] bytes)。它基于原始答案中的代码,但是,对于 Java 11+ 编译的类中的 bytes[],上述答案中的原始代码将失败。此版本对于 Java 7、8、11、17 及更高版本的所有版本都是安全的。

于 2022-02-04T12:52:12.237 回答