我们都知道,Java编译器负责将.java文件编译成.class文件,class文件存储的是java字节码,与.java文件无关(只要你愿意写一个编译器,也可以将别的语言写的源代码编译成.class文件),本文准备详细解剖class文件的内部结构,并且把class文件结构读取并显示出来。
Class文件的格式由JVM规范规定,一共有以下部分: 1. magic number,必须是0xCAFEBABE,用于快速识别是否是一个class文件。 2. version,包括major和minor,如果版本号超过了JVM的识别范围,JVM将拒绝执行。 3. constant pool,常量池,存放所有用到的常量。 4. access flag,定义类的访问权限。 5. this class和super class,指示如何找到this class和super class。 6. interfaces,存放所有interfaces。 7. fields,存放所有fields。 8. methods,存放所有methods。 9. attributes,存放所有attributes。
先写一个Test.java:
package example.test;
public final class TestClass { public int id = 1234567; public void test() {} }
然后编译,放在C:\example\test\Test.class。
我们用Java来读取和分析class,ClassAnalyzer的功能便是读取Test.class,分析结构,然后显示出来:
package classfile.format;
import java.io.*;
public class ClassAnalyzer {
public static void main(String[] args) { DataInputStream input = null; try { input = new DataInputStream(new BufferedInputStream(new FileInputStream( "C:\\example\\test\\TestClass.class" ))); analyze(input); } catch(Exception e) { System.out.println("Analyze failed!"); } finally { try { input.close(); } catch(Exception e) {} } }
public static void analyze(DataInputStream input) throws IOException { // read magic number: int magic = input.readInt(); if(magic==0xCAFEBABE) System.out.println("magic number = 0xCAFEBABE"); else throw new RuntimeException("Invalid magic number!"); // read minor version and major version: short minor_ver = input.readShort(); short major_ver = input.readShort(); System.out.println("Version = " + major_ver + "." + minor_ver); // read constant pool: short const_pool_count = input.readShort(); System.out.println("constant pool size = " + const_pool_count); // read each constant: for(int i=1; i<const_pool_count; i++) { analyzeConstant(input, i); } }
public static void analyzeConstant(DataInputStream input, int index) throws IOException { byte flag = input.readByte(); // for read: byte n8; short n16; int n32; long n64; float f; double d; byte[] buffer; System.out.println("\nconst index = " + index + ", flag = " + (int)flag); switch(flag) { case 1: // utf-8 string System.out.println(" const type = Utf8"); n16 = input.readShort(); System.out.println(" length = " + n16); buffer = new byte[n16]; input.readFully(buffer); System.out.println(" value = " + new String(buffer)); break; case 3: // integer System.out.println(" const type = Integer"); n32 = input.readInt(); System.out.println(" value = " + n32); break; case 4: // float System.out.println(" const type = Float"); f = input.readFloat(); System.out.println(" value = " + f); break; case 5: // long System.out.println(" const type = Long"); n64 = input.readLong(); System.out.println(" value = " + n64); break; case 6: // double System.out.println(" const type = Double"); d = input.readDouble(); System.out.println(" value = " + d); break; case 7: // class or interface reference System.out.println(" const type = Class"); n16 = input.readShort(); System.out.println(" index = " + n16 + " (where to find the class name)"); break; case 8: // string System.out.println(" const type = String"); n16 = input.readShort(); System.out.println(" index = " + n16); break; case 9: // field reference System.out.println(" const type = Fieldref"); n16 = input.readShort(); System.out.println("class index = " + n16 + " (where to find the class)"); n16 = input.readShort(); System.out.println("nameAndType = " + n16 + " (where to find the NameAndType)"); break; case 10: // method reference System.out.println(" const type = Methodref"); n16 = input.readShort(); System.out.println("class index = " + n16 + " (where to find the class)"); n16 = input.readShort(); System.out.println("nameAndType = " + n16 + " (where to find the NameAndType)"); break; case 11: // interface method reference System.out.println(" const type = InterfaceMethodref"); n16 = input.readShort(); System.out.println("class index = " + n16 + " (where to find the interface)"); n16 = input.readShort(); System.out.println("nameAndType = " + n16 + " (where to find the NameAndType)"); break; case 12: // name and type reference System.out.println(" const type = NameAndType"); n16 = input.readShort(); System.out.println(" name index = " + n16 + " (where to find the name)"); n16 = input.readShort(); System.out.println(" descripter = " + n16 + " (where to find the descriptor)"); break; default: throw new RuntimeException("Invalid constant pool flag: " + flag); } } }
输出结果为:
magic number = 0xCAFEBABE Version = 48.0 constant pool size = 22
const index = 1, flag = 1 const type = Utf8 length = 22 value = example/test/TestClass
const index = 2, flag = 7 const type = Class index = 1 (where to find the class name)
const index = 3, flag = 1 const type = Utf8 length = 16 value = java/lang/Object
const index = 4, flag = 7 const type = Class index = 3 (where to find the class name)
const index = 5, flag = 1 const type = Utf8 length = 2 value = id
const index = 6, flag = 1 const type = Utf8 length = 1 value = I
const index = 7, flag = 1 const type = Utf8 length = 6 value = <init>
const index = 8, flag = 1 const type = Utf8 length = 3 value = ()V
const index = 9, flag = 1 const type = Utf8 length = 4 value = Code
const index = 10, flag = 12 const type = NameAndType name index = 7 (where to find the name) descripter = 8 (where to find the descriptor)
const index = 11, flag = 10 const type = Methodref class index = 4 (where to find the class) nameAndType = 10 (where to find the NameAndType)
const index = 12, flag = 3 const type = Integer value = 1234567
const index = 13, flag = 12 const type = NameAndType name index = 5 (where to find the name) descripter = 6 (where to find the descriptor)
const index = 14, flag = 9 const type = Fieldref class index = 2 (where to find the class) nameAndType = 13 (where to find the NameAndType)
const index = 15, flag = 1 const type = Utf8 length = 15 value = LineNumberTable
const index = 16, flag = 1 const type = Utf8 length = 18 value = LocalVariableTable
const index = 17, flag = 1 const type = Utf8 length = 4 value = this
const index = 18, flag = 1 const type = Utf8 length = 24 value = Lexample/test/TestClass;
const index = 19, flag = 1 const type = Utf8 length = 4 value = test
const index = 20, flag = 1 const type = Utf8 length = 10 value = SourceFile
const index = 21, flag = 1 const type = Utf8 length = 14 value = TestClass.java
我们暂时只读取到constant pool,后面的信息下次再继续 :)
|