`

HelloWorld.class 文件的解读

    博客分类:
  • JVM
阅读更多

这一篇主要针对最简单的HelloWorld的class文件进行分析,按照上一篇文章的结构去实例化的分析一个class文件。

下面是java源文件

 

 

public class HelloWorld{
	public static void main(String [] arvgs){
	  System.out.println("hello world");
}
}

 

 运行 javac HelloWorld.java 得到的class 文件如下:

 

00000000h: CA FE BA BE 00 00 00 2E 00 1D 0A 00 06 00 0F 09 ; 漱壕............
00000010h: 00 10 00 11 08 00 12 0A 00 13 00 14 07 00 15 07 ; ................
00000020h: 00 16 01 00 06 3C 69 6E 69 74 3E 01 00 03 28 29 ; .....<init>...()
00000030h: 56 01 00 04 43 6F 64 65 01 00 0F 4C 69 6E 65 4E ; V...Code...LineN
00000040h: 75 6D 62 65 72 54 61 62 6C 65 01 00 04 6D 61 69 ; umberTable...mai
00000050h: 6E 01 00 16 28 5B 4C 6A 61 76 61 2F 6C 61 6E 67 ; n...([Ljava/lang
00000060h: 2F 53 74 72 69 6E 67 3B 29 56 01 00 0A 53 6F 75 ; /String;)V...Sou
00000070h: 72 63 65 46 69 6C 65 01 00 0F 48 65 6C 6C 6F 57 ; rceFile...HelloW
00000080h: 6F 72 6C 64 2E 6A 61 76 61 0C 00 07 00 08 07 00 ; orld.java.......
00000090h: 17 0C 00 18 00 19 01 00 0B 68 65 6C 6C 6F 20 77 ; .........hello w
000000a0h: 6F 72 6C 64 07 00 1A 0C 00 1B 00 1C 01 00 0A 48 ; orld...........H
000000b0h: 65 6C 6C 6F 57 6F 72 6C 64 01 00 10 6A 61 76 61 ; elloWorld...java
000000c0h: 2F 6C 61 6E 67 2F 4F 62 6A 65 63 74 01 00 10 6A ; /lang/Object...j
000000d0h: 61 76 61 2F 6C 61 6E 67 2F 53 79 73 74 65 6D 01 ; ava/lang/System.
000000e0h: 00 03 6F 75 74 01 00 15 4C 6A 61 76 61 2F 69 6F ; ..out...Ljava/io
000000f0h: 2F 50 72 69 6E 74 53 74 72 65 61 6D 3B 01 00 13 ; /PrintStream;...
00000100h: 6A 61 76 61 2F 69 6F 2F 50 72 69 6E 74 53 74 72 ; java/io/PrintStr
00000110h: 65 61 6D 01 00 07 70 72 69 6E 74 6C 6E 01 00 15 ; eam...println...
00000120h: 28 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 ; (Ljava/lang/Stri
00000130h: 6E 67 3B 29 56 00 21 00 05 00 06 00 00 00 00 00 ; ng;)V.!.........
00000140h: 02 00 01 00 07 00 08 00 01 00 09 00 00 00 1D 00 ; ................
00000150h: 01 00 01 00 00 00 05 2A B7 00 01 B1 00 00 00 01 ; .......*?.?...
00000160h: 00 0A 00 00 00 06 00 01 00 00 00 01 00 09 00 0B ; ................
00000170h: 00 0C 00 01 00 09 00 00 00 25 00 02 00 01 00 00 ; .........%......
00000180h: 00 09 B2 00 02 12 03 B6 00 04 B1 00 00 00 01 00 ; ..?...?.?....
00000190h: 0A 00 00 00 0A 00 02 00 00 00 03 00 08 00 04 00 ; ................
000001a0h: 01 00 0D 00 00 00 02 00 0E                      ; .........

 

 

接下去开始分析每一个自己的含义:

 

  1.  0X CA FE BA BE  四个字节:表示魔数(magic),作用在于区分是否是class 文件;
  2.  0X 00 00 两个字节:表示此版本号(minor_version),
  3.  0X 00 2E 两个字节:表示主版本号(major_version),可以发现jdk 1.6.0_24 的主版本号为 46,

  4.  0X 00 1D 两个字节:表示常量池的个数(constant_pool_count),这里说明共有28个常量(要减1),常量池列表的索引从1开始,没有索引为0的常量,但是0索引也被计数在数量中,所有需要减1;接下去开始是常量池的信息了,还记得cp_info 的结构吗?第一个字节为tag,后面根据tag值不一样而不一样;

  5. 第一个常量:tag=0X 0A 为一个 constant_methodref 类型(对一个类中申明的方法的符号引用),根据它的定义,后面四个字节属于它,class_index=0X00 06,name_and_type_index=0X00 0F ; 
         (1)、首先看class_index,顾名思义,表示这个方法所属于的类在常量池中的索引,查看第6号常量池,是一个constant_class,正是我们想要的,根据constant_class的定义;跟踪到22号常量池,可以看到这个方法属于Object类;
          (2)、第二个name_and_type_index:表示这个方法所对应的name_and_type类型在常量池中的索引;查看第15号常量池,正好是我们需要的constant_namdAndType类型的定义,继续查看constant_namdAndType的定义,发现名字和描述符,分别指向第7号和8号常量池,即 void init();方法。

  6. 第二个常量:tag=0X 09为一个constant_Fieldref 类型(对一个字段的符号的引用),根据它的定义后面四个字节属于它,class_index=0X00 10 , name_and_type_index = 0X 00 11
         (1)、同样先看class_index:表示这个字段所属的类,跟踪到16号常量池为constant_Class类型,并且指向第23号常量池,看23号常量池,表示这个字段属于 java/lang/System方法;
         (2)、name_and_type_index :表示name_and_type类型在常量池中的索引;查看17号常量池为constant_namdAndType类型,指向24、25号常量,表示字段名字为out,类型为 Ljava/io/PrintStream
     
  7. 第三个常量:tag=0X 08, 为一个constant_String 类型(String 类型的字符串),根据它的定义后面的两个字节属于它,String_index=0X00 12,查看第18号常量池 为“hello world” 正是我们想要的输出的内容;

  8. 第四个常量:tag=0X 0A, 为一个 constant_methodref 类型(对一个类中申明的方法的符号引用),根据它的定义,后面四个字节属于它,class_index=0X00 13,name_and_type_index=0X00 14
         (1)、首先看class_index,顾名思义,表示这个方法所属于的类在常量池中的索引,查看第19号常量池,是一个constant_class,正是我们想要的,根据constant_class的定义;跟踪到26号常量池,可以看到这个方法属于java/io/PrintStream类;
          (2)、第二个name_and_type_index:表示这个方法所对应的name_and_type类型在常量池中的索引;查看第20号常量池,正好是我们需要的constant_namdAndType类型的定义,继续查看constant_namdAndType的定义,发现名字和描述符,分别指向第27号和28号常量池,即 void println( (Ljava/lang/String;)方法。

  9. 第五个常量:tag=OX 07,为一个constant_Class类型(对类或者接口的符号引用),根据它的定义后面的2个字节属于它,name_index=0X00 15,查看第21号常量,为HelloWorld ,表示我们写的类HelloWorld;

  10. 第六个常量:tag=0X 07,为一个constant_Class类型(对类或者接口的符号引用),根据它的定义后面的2个字节属于它,name_index=0X00 16,查看第22号常量,为 java/lang/Object ,表示object类;

  11. 第七个常量:tag=0X 01,为一个constant_UTF8类型(utf8编码的字符串),根据它的定义后面的长度可变,length=0X00 06 表示后面有6个字节属于它的内容:bytes=0X 3C 69 6E 69 74  3E 即是:<init>;由前面可以知道为object的intit方法;

  12. 第八个常量:tag=0X 01,为一个constant_UTF8类型(utf8编码的字符串),根据它的定义后面的长度可变,length=0X00 03 表示后面有3个字节属于它的内容:bytes=0X 28 29 56  即是: ()V;由前面可以知道,这个表示object的intit方法的描述符:表示没有参数,返回值为void;

  13. 第九个常量:tag=0X 01,为一个constant_UTF8类型(utf8编码的字符串),根据它的定义后面的长度可变,length=0X00 04 表示后面有4个字节属于它的内容:bytes=0X  43 6F 64 65  即是: Code, 

  14. 第10个常量:tag=0X 01,为一个constant_UTF8类型(utf8编码的字符串),根据它的定义后面的长度可变,length=0X00 0F 表示后面有15个字节属于它的内容:bytes=0X 4C 69 6E 65 4E 75 6D 62 65 72 54 61 62 6C 65  即是: LineNumberTable  ; 

  15. 第11个常量:tag=0X 01,为一个constant_UTF8类型(utf8编码的字符串),根据它的定义后面的长度可变,length=0X00 04表示后面有4个字节属于它的内容:bytes=0X 6D 61 69 6E 即是: main ;表示main()方法;

  16. 第12个常量:tag=0X 01,为一个constant_UTF8类型(utf8编码的字符串),根据它的定义后面的长度可变,length=0X00 16表示后面有22个字节属于它的内容:bytes=0X 28 5B 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72  69 6E 67 3B 29 56   即是:  ([Ljava/lang/String;)V;有前面可以这个这个表示main方法的描述符,即返回值为void,参数为 string[] ([Ljava/lang/String; 中的第一个[表示一维数组),

  17. 第13个常量:tag=0X 01,为一个constant_UTF8类型(utf8编码的字符串),根据它的定义后面的长度可变,length=0X00 0A表示后面有10个字节属于它的内容:bytes=0X 53 6F 75 72 63 65 46 69 6C 65              即是: SourceFile ;

  18. 第14个常量:tag=0X 01,为一个constant_UTF8类型(utf8编码的字符串),根据它的定义后面的长度可变,length=0X00 0F表示后面有15个字节属于它的内容:bytes=0X  48 65 6C 6C 6F 57 6F 72 6C 64 2E 6A 61 76 61  即是: HelloWorld.java,可以知道这个是我们的文件名字;

  19. 第15个常量:tag=0X 0C,为一个constant_namdAndType类型(字段方法的部分符号引用),根据它的定义后面的4个字节属于它,name_index=0X00 07,descriptor_index=00 08;由前面可以知道,这个是对object的intit方法的总体描述,包含名字和描述符;

  20. 第16个常量:tag=0X 07,为一个constant_Class类型(对类或者接口的符号引用),根据它的定义后面的2个字节属于它,name_index=0X00 17,查看23号常量池,表示java/lang/System类;

  21. 第17个常量:tag=0X 0C,为一个constant_namdAndType类型(字段方法的部分符号引用),根据它的定义后面的4个字节属于它,name_index=0X00 18,descriptor_index=00 19,由前面可以知道,这个是对 Ljava/io/PrintStream类型的out字段的描述;

  22. 第18个常量:tag=0X 01,为一个constant_UTF8类型(utf8编码的字符串),根据它的定义后面的长度可变,length=0X00 0B表示后面有11个字节属于它的内容:bytes=0X 68 65 6C 6C 6F 20 77 6F 72 6C 64         即是: hello world,我们想要输出的常量

  23. 第19个常量:tag=0X 07,为一个constant_Class类型(对类或者接口的符号引用),根据它的定义后面的2个字节属于它,name_index=0X00 1A,查看26号常量池,表示java/io/PrintStream类;

  24. 第20个常量:tag=0X 0C,为一个constant_namdAndType类型(字段方法的部分符号引用),根据它的定义后面的4个字节属于它,name_index=0X00 1B,descriptor_index=00 1C,查看27、28号常量,是对println  方法的描述,表示返回值为void,参数为一个String;

  25. 第21个常量:tag=0X 01,为一个constant_UTF8类型(utf8编码的字符串),根据它的定义后面的长度可变,length=0X00 0A表示后面有10个字节属于它的内容:bytes=48 65 6C 6C 6F 57 6F 72 6C 64  
     即是: HelloWorld ;表示我们的类名
           
  26. 第22个常量:tag=0X 01,为一个constant_UTF8类型(utf8编码的字符串),根据它的定义后面的长度可变,length=0X00 10表示后面有16个字节属于它的内容:bytes= 6A 61 76 61 2F 6C 61 6E 67 2F 4F 62 6A 65 63 74  即是:  java/lang/Object , 

  27. 第23个常量:tag=0X 01,为一个constant_UTF8类型(utf8编码的字符串),根据它的定义后面的长度可变,length=0X00 10表示后面有16个字节属于它的内容:bytes=  6A 61 76 61 2F 6C 61 6E 67 2F 53 79 73 74 65 6D  即是:  java/lang/System

  28. 第24个常量:tag=0X 01,为一个constant_UTF8类型(utf8编码的字符串),根据它的定义后面的长度可变,length=0X00 03表示后面有3个字节属于它的内容:bytes=   6F 75 74    即是:out 

  29. 第25个常量:tag=0X 01,为一个constant_UTF8类型(utf8编码的字符串),根据它的定义后面的长度可变,length=0X00 15表示后面有21个字节属于它的内容:bytes=  4C 6A 61 76 61 2F 69 6F 2F 50 72 69 6E 74 53 74 72 65 61 6D 3B    即是: Ljava/io/PrintStream;

  30. 第26个常量:tag=0X 01,为一个constant_UTF8类型(utf8编码的字符串),根据它的定义后面的长度可变,length=0X00 13表示后面有19个字节属于它的内容:bytes=6A 61 76 61 2F 69 6F 2F 50 72 69 6E 74 53 74 72  65 61 6D    即是:  java/io/PrintStream

  31. 第27个常量:tag=0X 01,为一个constant_UTF8类型(utf8编码的字符串),根据它的定义后面的长度可变,length=0X00 07表示后面有7个字节属于它的内容:bytes=00000116h: 70 72 69 6E 74 6C 6E   即是: println   

  32. 第28个常量:tag=0X 01,为一个constant_UTF8类型(utf8编码的字符串),根据它的定义后面的长度可变,length=0X00 15表示后面有7个字节属于它的内容:bytes= 28 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69  6E 67 3B 29 56 即是:  (Ljava/lang/String;)V,至此常量池的解析就结束了。

  33. 0X 00 21 两个字节: 表示 access_flags,这里为0x0021。根据access_flags表可以查到,该值是0x00200x0001两者的和,即该类的修饰符为ACC_PUBLIC+ACC_SUPER,前者表示该类是public类型,后者表示采用invokespecial指令特殊处理对超类的调用

  34. 0X 00 05 两个字节:表示this_class;它是一个对常量池表项的索引,指向5号常量池;查看5号常量池正是HelloWorld

  35.  0X 00 06 两个字节:表示 super_class:它也是一个对常量池表象的索引,指向6号常量池;表示为object类;

  36. 0X 00 00 两个字节:表示interfaces_count,表示直接实现或者该解决扩展的超接口为0个;没有实现任何接口

  37. 0X 00 00 两个字节:表示feild_count,表示该类没有申明字段;

  38. 0X 00 02 两个字节:表示method_count,表示该类申明了2个方法,接下去是这两个方法的相信信息

  39. 第一个method_info的结构:根据上一章节的内容,接下去的2个字节表示
     (1)、access_flags=0X 00 01,表示是一个ACC_PUBLIC 的方法,
     (2)、在两个字节0X 00 07 表示 name_index,表示常量池中第7个常量为<init>,即是 <init>方法,
     (3)、在接下两个字节 0X00 08 表示desciptor_index,表示常量池第8个常量为 ()V ,即是没有参数,返回值为void;,
     (4)、在接下去两个字节0X 00 01 表示attribute_count,表示有1个attribute,
     (5)、接下去表示一个attribute_info 的结构;查看attribute_info 的结构定义:

  40. attribute_info {        
     u2 attribute_name_index;
     u4 attribute_length;
     u1 info[attribute_length];
        }
       1)、所以 在接下去的两个字节 0X 00 09,查看第9好常量池为Code,然后code_attribute的定义:


    Code_attribute {        
            u2 attribute_name_index;
             u4 attribute_length;
             u2 max_stack;
             u2 max_locals;
             u4 code_length;
             u1 code[code_length];
             u2 exception_table_length;
             {       u2 start_pc;
                    u2 end_pc;
                    u2 handler_pc;
                    u2 catch_type;
             }       exception_table[exception_table_length];
             u2 attributes_count;
             attribute_info attributes[attributes_count];
        }
         
    在看这个结构体 attribute_name_index =0X 00 09,然后4个字节0X 00 00 00 1D表示长度 为29个字节说明接下去的29个字节属于这个属性,这里不暂时不展开。 

  41. 第二个method_info的结构:去掉前面的29个字节,
    (1)、接下去的2个字节表示access_flags=0X 00 09,表示是一个ACC_PUBLIC和ACC_STATIC 的方法,
    (2)、在两个字节0X 00 0B表示 name_index,表示常量池中第11个常量为main ,即是 main 方法,
    (3)、在接下两个字节 0X00 0C 表示desciptor_index,表示常量池第12个常量为([Ljava/lang/Str ing;)V,即是参数为String [],返回值为void;
    (4)、在接下去两个字节0X 00 01 表示attribute_count,表示有1个attribute,索引接下去表示一个attribute_info 的结构;所有查看attribute_info 的结构定义:
     
    attribute_info {        
     u2 attribute_name_index;
     u4 attribute_length;
     u1 info[attribute_length];
        }
     1)、所以 在接下去的两个字节 0X 00 09,查看第9好常量池为Code,然后code_attribute的定义:

    Code_attribute {
             u2 attribute_name_index;
             u4 attribute_length;
             u2 max_stack;
             u2 max_locals;
             u4 code_length;
             u1 code[code_length];
             u2 exception_table_length;
             {       u2 start_pc;
                    u2 end_pc;
                    u2 handler_pc;
                    u2 catch_type;
             }       exception_table[exception_table_length];
             u2 attributes_count;
             attribute_info attributes[attributes_count];
        }
     在看这个结构体 attribute_name_index =0X 00 09,然后4个字节0X 00 00 00 25表示长度 为37个字节说明接下去的37个字节属于这个属性,这里不暂时不展开。


  42. 去掉前面的37个字节,接下去的两个字节0X 00 01 表示接下去有一个attributes_info结构,参照前面的定义,(1)、0X 00 0D表示 attribute_name_index,13号常量为 SourceFile,表示属性名字为SourceFile; 该属性是一个可选的定长属性,并且在class文件中只有一个sourcefile_atrribute存在,对于给定的ClassFile结构的attributes列表中不能有多于一的SourceFile属性;查阅SourceFile_attribute表可知,
    sourceFile_attribute{
       U2  attribute_name_index ;
       U4  attribute_length;
       U2   sourcefile_index;
    }
     
     (1)、下面的
    4个字节为attribute_length项,其值为0x00 00 00 02,它表示在该项后面还有2个字节的信息,这个长度永远为2。
      (2)、根据SourceFile_attribute表,最后的这两个字节是sourcefile_index项,该项的值是一个对CONSTANT_Utf8_info结构的常量池表项的索引,其信息表示的是该Class文件的源文件名称。在这里值为0x00 0E,14号常量池表项存储的信息可解析为“HelloWorld.java”,这是该Class文件的源文件名称(不包括路径)

到此整个 HelloWord.class 解析结束。。下一节。我们将通过几个例子说明怎么利用读懂的class文件的格式。

 

 

本站支持 pay for your wishes

4
0
分享到:
评论

相关推荐

    Hello World the WebWork way

    public class HelloWorld implements Action { private String message; public String execute() { message = "Hello, World!\n"; message += "The time is:\n"; message += System.currentTimeMillis(); ...

    class文件解析案例

    每个class文件的结构遵循特定的规范,以确保JVM能够正确解读并加载。 1. **类文件结构**: - **魔数**:标识文件是否为合法的class文件,固定值0CAFEBABE。 - **版本号**:包括Minor和Major版本号,表示该class...

    生成JAR包的方法(打包后运行好像不快)

    10. **示例**:创建名为`Hello.jar`的JAR包,包含`HelloWorld.class`类,并使用清单文件`manifest.mf`: ```sh jar cvfm Hello.jar manifest.mf HelloWorld.class ``` 11. **清单文件示例**:清单文件通常包含...

    合工大Java实验报告.doc

    例如,`javac HelloWorld.java`会生成`HelloWorld.class`。然后,通过`java`命令运行程序,如`java HelloWorld`。实验还涉及了多包管理,创建了位于`a`包下的`A`类和`b`包下的`B`类,`B`类中实例化了`A`类的对象。 ...

    Java学习总结.pdf

    通过java命令运行编译后的.class文件。 ```bash java HelloWorld ``` 3. Java与数据库的交互: - 使用JDBC进行数据库操作: JDBC(Java Database Connectivity)是一种技术,允许Java程序通过JDBC API连接和...

    java入门之java基础语法.docx

    - 如果编译成功,则会在同一目录下生成`HelloWorld.class`文件。 - **运行程序**: - 输入`java HelloWorld`运行程序。 - 屏幕上将显示`Hello World`。 #### 五、Java基础语法要点 - **大小写敏感**:Java区分...

    LINUX下JAVA環境配置.pdf

    - **意义**:编译Java源代码文件,如果一切正常,会生成`HelloWorld.class`文件。 ##### 5.4 运行:`java HelloWorld` - **命令**:在终端中输入`java HelloWorld`。 - **意义**:运行编译后的Java程序,输出`...

    JAVA运行环境的安装[收集].pdf

    - 编译Java程序:`javac HelloWorld.java`,这将生成一个`HelloWorld.class`字节码文件。 - 运行Java程序:`java HelloWorld`,这将在控制台打印出"hello,java!"。 6. **Applet程序** - Applet是能在网页中运行...

    nmm.zip_nmm15.com_nmm30com_speechprk_world

    public class HelloWorld { public static void main(String[] args) { System.out.println("Hello, World!"); } } ``` 接下来,nmm15.com和nmm30com可能指的是两个不同的网站或者网络资源,或者是某种服务的...

    axis2发布web service

    &lt;parameter name="ServiceClass"&gt;helloWorld ``` 这里有几个关键点: - `&lt;service&gt;` 标签定义了 Web 服务的基本属性。 - `name` 属性指定了服务的名称。 - `scope` 属性设为 `application` 表示这是一个应用...

    中期开发平台说明文档-数据及业务层

    * Class:每个 Java 文件应该包含类定义,例如:public class HelloWorld { ... }。 * Class Fields:每个 Java 文件应该包含类字段定义,例如:private int count;。 DEMO 简介 在中期开发平台中,我们提供了一个...

    JDK开发工具命令集合

    示例:javac HelloWorld.java ``` - **常见选项** - `-g`:生成完整的调试信息。 - `-g:none`:不生成任何调试信息。 - `-g:{lines,vars,source}`:仅生成特定类型的调试信息。 - `-nowarn`:忽略警告信息。...

    NetBeans IDE 6.0 Java Quick Start Tutorial

    public class HelloWorldApp { public static void main(String[] args) { System.out.println("Hello World!"); } } ``` 这一步骤演示了如何利用IDE提供的模板进行代码编写,是Java编程的基础实践之一。 #### ...

    Java编程入门.pdf

    编译器会生成.class文件,它包含了程序的字节码,可以被Java虚拟机(JVM)执行。 在对“Hello, World!”程序代码进行逐行解读时,本部分向初学者介绍了System.out.println()方法,这是向标准输出流(通常是控制台)...

    一个简单的C#脚本示例,它会输出"Hello, World!"到控制台

    #### 标题理解:“一个简单的C#脚本示例,它会输出'Hello, World!'到控制台” 该标题介绍了一个基础的C#程序示例,用于输出经典的“Hello, World!”字符串到控制台。这通常作为学习任何编程语言的第一个程序,用于...

    创建简单的WEB应用-maven-Tomcat版.doc

    response.getWriter().println("Hello, World!"); } } ``` 记得在`web.xml`中注册这个Servlet: ```xml &lt;servlet-name&gt;HelloWorldServlet &lt;servlet-class&gt;...

    京东T5级大牛带你解读Spring核心源码——1小时手写SpringMVC~

    &lt;property name="message" value="Hello World!" /&gt; ``` - **步骤二:定义前端控制器** 创建一个`DispatcherServlet`类,该类负责处理HTTP请求,并调用相应的控制器处理请求。 ```java public class ...

Global site tag (gtag.js) - Google Analytics