字节码文件(Class文件)

    xiaoxiao2022-07-05  155

    一、Java的平台无关性实现

           我们知道Java是“与平台无关”的,实现语言无关性的基础就是虚拟机和字节码存储格式:依靠class文件这种统一程序存储格式的字节码文件实现了语言无关性。保证了就算是不同语言,只要通过对应语言的编译器按虚拟机规范编译成class文件,这个class文件就能够被jvm加载。而不同的操作系统通过运行对应版本jvm来载入这个class文件运行,则实现了平台无关性。

    二、那我们有必要来了解下字节码文件,还是从程序入手

    public class HelloWorld{ public static void main(String []args){ System.out.println("Hello Java"); } }

    三、编译为字节码文件HelloWorld.class

    1、十六进制打开的部分展示:可以看出Class文件是一组以8位字节为基础单位的二进制流

    cafe babe 0000 0034 001d 0a00 0600 0f09 0010 0011 0800 120a 0013 0014 0700 1507 0016 0100 063c 696e 6974 3e01 0003 2829 5601 0004 436f 6465 0100 0f4c 696e 654e 756d 6265 7254 6162 6c65 0100 046d 6169 6e01 0016 285b 4c6a 6176 612f 6c61 6e67 .....

    2、重定向输出为txt文件:javap -v HelloWorld > HelloWorld.class.txt,部分展示:

    Classfile /C:/Users/asus/Desktop/HelloWorld.class Last modified 2019-5-22; size 425 bytes MD5 checksum b6b55b6a43521851a348c690e1e79095 Compiled from "HelloWorld.java" public class HelloWorld minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #6.#15 // java/lang/Object."<init>":()V #2 = Fieldref #16.#17 // java/lang/System.out:Ljava/io/PrintStream; #3 = String #18 // Hello Java .....

    3、大概了解了字节码文件的格式,那我们重点来解析它:

    (1)魔数 :开头4个字节的cafebabe(咖啡宝贝?java的杯子装咖啡的logo),表明这是一个可以被jvm执行的class文件,仅仅通过.class后缀名来辨别文件的类型是不安全的。基于安全考虑,采用魔数标识.class文件。

    (2)版本号:第5、6个字节0000表示次版本号,第7、8字节0034表示主版本号。

    (3)常量池 :接着就是常量池,首先是常量池的入口,是一个u2类型的数据,代表常量池的数量:这个从txt文件可以一眼看出有28个常量,而且是从1开始。从第9个字节1d是十六进制,转为10进制就是29,0位空出来了表示null,所以是28个常量。 

    常量池里存放的是字面常量(literal)和符号引用(symbolic references),字面量比较接近java中常量的概念,如文本字符串,声明为final的常量值等。符号引用属于编译原理方便的概念,包含以下三个常量;      3.1 类和接口的全限定名      3.2 字段的名称和描述符      3.3 方法的名称和描述符

       每一个常量都是一个表,或者说都有他们自己的结构,所有常量结构的共同点是它们的第一位都是一个u1类型的标志位,用来标志这个常量的类型。

        分析一个常量:第十个字节是0a-->10,这个是常量的标志类型,查表10(常量池的项目类型表 ):  表示类中方法的引用。看txt文件也可以知道: #1 = Methodref          #6.#15         // java/lang/Object."<init>":()V ,这个常量分析我没有深究,就此结束。

    (4)在常量池结束之后,紧接着是两个字节代表访问标志:识别这个Class是类还是接口,是不是public类型等等,查表可知。

    (5)类索引,父索引和接口索引集合都按顺序排在访问标志之后。

    (6)字段表集合:类级变量和实例化变量,包括作用域、修饰符等信息。

    (7)方法的表集合:访问标志和名称索引,描述符索引和属性表集合等。

    (8)方法中的代码在Code属性中,这也是最重要的一个属性,看txt文件,如下:

    { public HelloWorld(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 1: 0 public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=1, args_size=1 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #3 // String Hello Java 5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return LineNumberTable: line 3: 0 line 4: 8 }

    首先是默认自带的无参构造方法,然后是main()方法。

    (9)最后是输出:SourceFile: "HelloWorld.java"

     

     

    最新回复(0)