Class文件结构概述

Class文件结构概述,第1张

Class文件结构概述

Java语言规范

字节码文件的跨平台性
  1. Java语言:跨平台的语言(write once,run anywhere)
    当java源代码成功编译成字节码后,如果想在不同的平台上面运行,则无须再次编译。
  2. Java虚拟机:跨语言的平台
    1. Java虚拟机 不和包括Java在内的任何语言绑定,它只与“Class文件”这种特定的二进制格式的文件关联。无论使用何种语言进行软件开发,只要能将源文件编译为正确的Class文件,那么这种语言就可以在Java虚拟机上执行。可用说,统一而强大的Class文件结构,就是Java虚拟机的基石和桥梁。
    2. 所有的JVM全部遵守Java虚拟机规范,也就是说所有的JVM环境都是一样的,这样一来字节码文件可以在各种JVM上运行。
  3. 想要让一个Java程序正确地运行在JVM中,Java源码就必须要被编译成为符合JVM规范的字节码。
    • 前端编译器的主要任务就是负责将符合Java语法规范的Java代码转换为符合JVM规范的字节码文件。
    • javac是一种能够将Java源码编译为字节码的前端编译器。
    • javac编译器在将Java源码编译成为一个有效的字节码文件过程中经历了4个步骤,分别是词法解析、语法解析、语义解析以及生成字节码。
  4. Oralce的JDK软件包括两部分内容:
    1. 一部分是将Java源代码编译成Java虚拟机的指令集的编译器
    2. 另一部分用于实现Java虚拟机的运行时环境。
Java的前端编译器

Java源代码的编译结果是字节码,那么肯定需要有一种编译器能够将Java源码编译为字节码,承担这个重要责任的就是配置在path环境变量中的javac编译器。javac是一种能够将Java源代码编译为字节码的前端编译器。

HotSpot VM并没有强制要求前端编译器只能使用javac来编译字节码,其实只要编译结果符合JVM规范都可以被JVM所识别。在Java的前端编译器领域,除了javac之外,还有一种被大家经常用到的前端编译器,那就是内置在Eclipse中的ECJ(Eclipse Compiler for Java)编译器。和Javac的全量式编译不同,ECJ是一种增量式编译器。

  • 在Eclipse中,当开发人员编写完成代码之后,使用“Ctrl+S”快捷键时,ECJ编译器所采用的编译方案是把未编译部分的源码逐行进行编译,而非每次全量编译。因此ECJ的编译效率会比javac更加迅速和高效。当然编译质量和javac相比大致还是一样的。
  • ECJ不仅是Eclipse的默认内置前端编译器,在Tomcat中同样也是使用ECJ编译器来编译jsp文件。由于ECJ编译器是采用GPLv2的开源协议进行源代码公开,所以,大家可以登录eclipse的官网下载ECJ编译器的源码进行二次开发。
  • 默认情况下,IntelliJ IDEA使用javac编译器。(还可以自己设置为AspectJ编译器)。

前端编译器并不会直接涉及编译优化等方面的技术,而是将这些具体优化细节移交给HotSpot的JIT编译器负责。

透过字节码看代码执行

代码:

package bytecode;

class Father {
    int x = 10;

    public Father() {
        this.print();
        x = 20;
    }

    public void print() {
        System.out.println("Father.x = " + x);
    }
}

class Son extends Father {
    int x = 30;
    public Son() {
        this.print();
        x = 40;
    }

    public void print() {
        System.out.println("Son.x = " + x);
    }
}

public class SonTest {

    public static void main(String[] args) {
        Father f = new Son();
        System.out.println(f.x);
    }
}
  • Father类的构造器的字节码:
     0 aload_0
     1 invokespecial #1  : ()V>
     4 aload_0
     5 bipush 10
     7 putfield #2 
    10 aload_0
    11 invokevirtual #3 
    14 aload_0
    15 bipush 20
    17 putfield #2 
    20 return
    
    从Father类的字节码可以看出三点:
    1. 父类构造器的调用,是在子类构造器中被调用的。
    2. 成员变量的默认初始化是在构造器中进行的。
    3. 构造器中成员变量的默认初始化先于显式初始。
  • Son类的构造器的字节码:
     0 aload_0
     1 invokespecial #1  : ()V>
     4 aload_0
     5 bipush 30
     7 putfield #2 
    10 aload_0
    11 invokevirtual #3 
    14 aload_0
    15 bipush 40
    17 putfield #2 
    20 return
    
    从Son类的构造器的字节码可以看出:
    1. 父类Father的构造器是在子类Son的构造器中被调用的。
    2. 成员变量x被默认赋值为30的行为也是在构造中进行的。
    3. 成员变量x被显式赋值为40的行为是在默认赋值行为之后发生的。
虚拟机的基石:Class文件
  • 字节码文件里是什么?
    源代码经过编译器编译之后便会生成一个字节码文件,字节码是一种二进制的类文件,它的内容是JVM的指令,而不像C,C++经由编译器直接生成机器码。
  • 什么是字节码指令?
    Java虚拟机的指令由一个字节长度的,代表着某种特定 *** 作含义的 *** 作码以及跟随其后的零至多个代表此 *** 作所需参数的 *** 作数所构成。虚拟机中的许多指令并不包含 *** 作数,只是一个 *** 作码。
查看字节码文件的方式
  • 方式1,在IntelliJ Idea中使用Jclasslib插件
  • 方式2,使用JDK自带的命令行工具javap

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/4996577.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-11-14
下一篇 2022-11-14

发表评论

登录后才能评论

评论列表(0条)

保存