2009-11-18 133 views

回答

7

如果您知道the spec,很容易就可以做到。

继承人快速和肮脏的演示程序是如何做到这一点:

package test; 

import java.io.DataInputStream; 
import java.io.IOException; 
import java.io.InputStream; 
import java.util.*; 

public class Main { 

    private static Map<Integer, String> strings = new HashMap<Integer, String>(); 
    private static Set<Integer> classes = new HashSet<Integer>(); 
    private static int indexCorrection = 0; // for correcting indexes to constant pools with long and double entries 

    public static void main(String[] args) throws Exception { 
     printReferencedClassesFromClass(String.class); 
    } 

    private static void printReferencedClassesFromClass(Class<?> c) throws IOException { 
     saveReferencedClassesFromStream(c.getResourceAsStream(c.getSimpleName() + ".class")); 
     printReferencedClasses(); 
    } 

    private static void printReferencedClasses() { 
     for (Integer index : classes) 
      System.out.println(strings.get(index)); 
    } 

    private static void saveReferencedClassesFromStream(InputStream stream) throws IOException { 
     DataInputStream dataStream = new DataInputStream(stream); 
     skipHeader(dataStream); 
     saveReferencedClassesFromConstantPool(dataStream); 
    } 

    private static void skipHeader(DataInputStream dataInputStream) throws IOException { 
     readU4(dataInputStream); // magic byte 
     readU2(dataInputStream); // minor version 
     readU2(dataInputStream); // major version 
    } 

    private static void saveReferencedClassesFromConstantPool(DataInputStream stream) throws IOException { 
     int poolSize = readU2(stream); 
     for (int n = 1; n < poolSize - indexCorrection; n++) 
      savePoolEntryIfIsClassReference(n, stream); 
    } 

    private static void savePoolEntryIfIsClassReference(int index, DataInputStream stream) throws IOException { 
     int tag = readU1(stream); 
     switch (tag) { 
     case 1: // Utf8 
      saveStringFromUtf8Entry(index, stream); 
      break; 
     case 7: // Class 
      saveClassEntry(stream); 
      break; 
     case 8: // String 
     case 16: // MethodType 
      readU2(stream); 
      break; 
     case 3: // Integer 
     case 4: // Float 
      readU4(stream); 
      break; 
     case 5: // Long 
     case 6: // Double 
      readU4(stream); 
      readU4(stream); 
      indexCorrection++; 
      break; 
     case 9: // Fieldref 
     case 10: // Methodref 
     case 11: // InterfaceMethodref 
     case 12: // NameAndType 
     case 18: // InvokeDynmaic 
      readU2(stream); 
      readU2(stream); 
      break; 
     case 15: // MethodHandle 
      readU1(stream); 
      readU2(stream); 
      break; 
     } 
    } 

    private static void saveClassEntry(DataInputStream stream) throws IOException { 
     classes.add(readU2(stream)); 
    } 

    private static void saveStringFromUtf8Entry(int index, DataInputStream stream) throws IOException { 
     strings.put(index + indexCorrection, readString(stream)); 
    } 

    private static String readString(DataInputStream stream) throws IOException { 
     return stream.readUTF(); 
    } 

    private static int readU1(DataInputStream stream) throws IOException { 
     return stream.readUnsignedByte(); 
    } 

    private static int readU2(DataInputStream stream) throws IOException { 
     return stream.readUnsignedShort(); 
    } 

    private static int readU4(DataInputStream stream) throws IOException { 
     return stream.readInt(); 
    } 

} 

输出是:

test/Main 
java/lang/Object 
java/io/BufferedInputStream 
java/util/Iterator 
java/io/FileInputStream 
java/lang/System 
java/lang/Integer 
java/lang/Exception 
java/lang/String 
java/io/File 
java/util/Map 
java/util/HashMap 
java/io/IOException 
java/util/Set 
java/io/DataInputStream 
java/io/PrintStream 
java/util/HashSet 
java/lang/Throwable 
+0

不幸的是不是每次都工作,必须是某处的错误。在guava-17上的'ImmutableSetMultimap.class'上试试。它会将“无效的密钥计数”字符串视为类名称。另外,尝试在'switch(tag)'中添加'default'部分:它会在常量池的末尾用'tag == 0'触发 – eprst 2016-08-24 21:37:51

+0

我修复了这个bug并修改了代码以使用java 8。如果这些格式将其他标签类型添加到常量池中,则可以再次使用较新的类文件格式进行打破。 – Arne 2016-08-26 15:14:13

1

您是否试过JDepend?它分析类文件以确定引用。

0

我不知道最好的方法是什么。我知道有2个API可能有帮助。

您可以尝试jclasslib,用于检查的.class用于分析和操纵的.class文件的文件
http://www.ej-technologies.com/products/jclasslib/resources.html
或BCEL(字节码工程库)。
http://jakarta.apache.org/bcel/

我的理解是任何在类中使用的类(只要字段,参数,返回类型等)都是在constant_pool中定义的。所以你可能想要阅读。
http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html

相关问题