当前位置: 首页 > 科技观察

JVM真香系列:Java文件转.Class文件

时间:2023-03-20 17:27:02 科技观察

什么是JVM?JVM全称JavaVirtualMachine,也就是我们熟悉的Java虚拟机。它可以识别.class后缀的文件,并可以解析它的指令,最后调用操作系统上的函数来完成我们想要的操作。可能有些朋友学过C++,用C++开发的程序编译成二进制文件后可以直接执行,操作系统也能识别。但是我们打开的Java程序就不一样了。使用javac命令编译.class文件后,操作系统无法识别。需要先转换为对应的JVM,操作系统才能识别。为什么我们不能像C++那样直接在操作系统上运行编译好的二进制文件呢?而不是在程序和操作系统之间的中间层创建一个虚拟机?这就是JVM的优势所在。大家都知道Java是一种抽象程度特别高的语言,提供了自动内存管理等一系列特性。直接在操作系统上实现这些特性是不可能的,所以需要JVM做一系列的转换。大家开始学习Java的时候,就知道有一个WriteOnce,RunEverywhere。也就是我们写一个java文件,编译成.class文件后,就可以在各种系统中运行了。其实这里有个前提,我们需要在相应的操作系统中安装相应的JVM,然后我们的.class文件才能运行。比如Windows操作系统有对应的JDK安装版本,Linux也有对应的JDK安装版本。认识JDKJava开发工具包(JDK)是Sun(已被Oracle收购)为Java开发人员提供的软件开发工具包。自Java推出以来,JDK已成为使用最广泛的JavaSDK(软件开发工具包)。据非官方调查,JDK8是目前使用人数最多的版本。JDK14将在4月和7月收到安全更新,然后被9月到期的非LTS版本的JDK15取代。JDK14包括16个新特性,例如JDKFlightRecorder事件流、模式匹配、switch表达式等特性。从JDK9开始,Oracle采用了新的发布周期:每6个月发布一个版本,每3年发布一个LTS版本。JDK14是继JDK9之后发布的第四个版本。该版本为非LTS版本,最新的LTS版本为JDK11。以下是JDK版本状态。只是熟悉它。密切关注JDK版本更新和新功能。官网地址:https://www.oracle.com/java/关于JDK安装这里省略。JDK、JRE、JVM的关系前面已经提到了JDK和JVM的相关概念。JRE完整的JavaRuntimeEnvironment是运行用Java语言编写的程序不可或缺的运行环境。也正是通过它,Java开发者可以将自己开发的程序发布给用户使用。三者之间是什么关系?三者的关系请参考官网https://docs.oracle.com/javase/8/docs/index.html。JDK包括JRE和JDK,JRE也包括JDK。作用域关系:JDK>JRE>JVM“.java”文件到“.class”文件`javac`命令写入一个HelloWorld.java文件内容是一个Java入口publicclassHelloWorld{publicstaticvoidmain(String[]args){System.out.println("Helloworld");}}打开CMD,进入当前目录,使用命令javacHelloWorld.java编译HelloWorld.class编译过程。这个javac命令进程到底做了什么?这些操作大致都是在javac后面完成的。这个过程1.词法分析阅读源码,逐字节阅读,找出我们定义的关键字(比如Java中的if,else,for,while等关键字,识别哪些if是合法的关键字,哪些是都不是),这是词法分析器进行词法分析的过程,结果就是从源码中找出归一化的Token流。2.语法分析使用语法分析器对词法分析后的Token流进行语法分析。这一步再次检查这些关键字组合是否符合Java语言规范(比如紧跟在if后面的是否有布尔判断表达式),词法分析的结果形成一棵符合Java语言规范的抽象语法树。3.语义分析语义分析是通过语义分析器进行的。语音分析主要是将一些难的复杂的语法转化为更简单的语法,得到最简单的语法(比如将foreach转化为for循环,带注解等),最后经过注解形成一个抽象的语法树,这个语法树就是更接近目标语言的语法规则。4.生成字节码通过字节码生成器生成字节码,根据注释语法抽象树生成字节码,即将一种数据结构转换为另一种数据结构。最后生成我们想要的.class文件。使用十六进制查看类文件的内容。我只使用记事本++。选择Text→Plug-in→Converter→ASCII->HEXclass文件开头是CAFEBABE。如果想了解这里的十六进制字节码的含义,可以参考https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.htmljavap查看类文件内容javap是一个Java类文件可以反编译(即反编译javac编译的文件)的decomposer,还可以查看java编译器生成的字节码。新建一个User.java源文件,用javac编译生成User.classs。packagecom.tian.demo.test;publicclassUser{privateintage=22;privateStringname="tian";publicintaddAge(){returnage=age+1;}publicstaticvoidmain(String[]args){}}使用javap指令javap-vUser.class>log.txt打开log.txtClassfile/D:/workspace/new/demo/src/main/java/com/tian/demo/test/User.classLastmodified2020-11-5;size441bytesMD5checksum2fa72d3f53bd9f138e0bfae82aba67e3Compiledfrom"User.java"publicclasscom.tian.demo.test.Userminorversion:0majorversion:52flags:ACC_PUBLIC,ACC_SUPERConstantpool:#1=Methodref#6.#21//java/lang/Object."":()V#2=Fieldref#5.#22//com/tian/demo/test/User.age:I#3=String#23//tian#4=Fieldref#5.#24//com/tian/demo/test/User.name:Ljava/lang/String;#5=Class#25//com/tian/demo/test/User#6=Class#26//java/lang/Object#7=Utf8age#8=Utf8I#9=Utf8name#10=Utf8Ljava/lang/字符串;#11=Utf8#12=Utf8()V#13=Utf8Code#14=Utf8LineNumberTable#15=Utf8addAge#16=Utf8()I#17=Utf8main#18=Utf8([Ljava/lang/String;)V#19=Utf8搜rceFile#20=Utf8User.java#21=NameAndType#11:#12//"":()V#22=NameAndType#7:#8//age:I#23=Utf8tian#24=NameAndType#9:#10//name:Ljava/lang/String;#25=Utf8com/tian/demo/test/User#26=Utf8java/lang/Object{publiccom.tian.demo.test.User();描述符:()vflags:ACC_PUBLICCode:stack=2,locals=1,args_size=10:aload_01:invokespecial#1//Methodjava/lang/Object."":()V4:aload_05:bipush227:putfield#2//字段:I10:aload_011:ldc#3//Stringtian13:putfield#4//Fieldname:Ljava/lang/String;16:returnLineNumberTable:line3:0line4:4line5:10publicintaddAge();descriptor:()Iflags:ACC_PUBLICCode:stack=3,locals=1,args_size=10:aload_01:aload_02:getfield#2//字段:I5:iconst_16:iadd7:dup_x18:putfield#2//字段:I11:ireturnLineNumberTable:line8:0publicstaticvoidmain(java.lang.String[]);描述符:([Ljava/lang/String;)Vflags:ACC_PUBLIC,ACC_STATICCode:stack=0,locals=1,args_size=10:returnLineNumberTable:line13:0}SourceFile:"User.java"幻数和类文件版本常量池访问标志类索引,父类索引,接口索引字段表集合方法表集合属性表集合然后JVM可以读取User.class文件进行解析等等以上一系列操作都是我们的Java文件到类文件。IT技术分享社区个人博客网址:https://programmerblog.xyz理解JVM什么是JVM?JVM的全称是JavaVirtualMachine,也就是我们熟悉的Java虚拟机。它可以识别.class后缀的文件,并可以解析它的指令,最后调用操作系统上的函数来完成我们想要的操作。可能有些朋友学过C++,用C++开发的程序编译成二进制文件后可以直接执行,操作系统也能识别。但是我们打开的Java程序就不一样了。使用javac命令编译.class文件后,操作系统无法识别。需要先转换为对应的JVM,操作系统才能识别。为什么我们不能像C++那样直接在操作系统上运行编译好的二进制文件呢?而不是在程序和操作系统之间的中间层创建一个虚拟机?这就是JVM的优势所在。大家都知道Java是一种抽象程度特别高的语言,提供了自动内存管理等一系列特性。直接在操作系统上实现这些特性是不可能的,所以需要JVM做一系列的转换。大家开始学习Java的时候,就知道有一个WriteOnce,RunEverywhere。也就是我们写一个java文件,编译成.class文件后,就可以在各种系统中运行了。其实这里有个前提,我们需要在相应的操作系统中安装相应的JVM,然后我们的.class文件才能运行。比如Windows操作系统有对应的JDK安装版本,Linux也有对应的JDK安装版本。认识JDKJava开发工具包(JDK)是Sun(已被Oracle收购)为Java开发人员提供的软件开发工具包。自Java推出以来,JDK已成为使用最广泛的JavaSDK(软件开发工具包)。据非官方调查,JDK8是目前使用人数最多的版本。JDK14将在4月和7月收到安全更新,然后被9月到期的非LTS版本的JDK15取代。JDK14包括16个新特性,例如JDKFlightRecorder事件流、模式匹配、switch表达式等特性。从JDK9开始,Oracle采用了新的发布周期:每6个月发布一个版本,每3年发布一个LTS版本。JDK14是继JDK9之后发布的第四个版本。该版本为非LTS版本,最新的LTS版本为JDK11。以下是JDK版本状态。只是熟悉它。密切关注JDK版本更新和新功能。官网地址:https://www.oracle.com/java/关于JDK安装这里省略。JDK、JRE、JVM的关系前面已经提到了JDK和JVM的相关概念。JRE完整的JavaRuntimeEnvironment是运行用Java语言编写的程序不可或缺的运行环境。也正是通过它,Java开发者可以将自己开发的程序发布给用户使用。三者之间是什么关系?三者的关系请参考官网https://docs.oracle.com/javase/8/docs/index.html。JDK包括JRE和JDK,JRE也包括JDK。作用域关系:JDK>JRE>JVM“.java”文件到“.class”文件`javac`命令写入一个HelloWorld.java文件内容是一个Java入口publicclassHelloWorld{publicstaticvoidmain(String[]args){System.out.println("Helloworld");}}打开CMD,进入当前目录,使用命令javacHelloWorld.java编译HelloWorld.class编译过程。这个javac命令进程到底做了什么?这些操作大致都是在javac后面完成的。这个过程1.词法分析阅读源码,逐字节阅读,找出我们定义的关键字(比如Java中的if,else,for,while等关键字,识别哪些if是合法的关键字,哪些是都不是),这是词法分析器进行词法分析的过程,结果就是从源码中找出归一化的Token流。2.语法分析使用语法分析器对词法分析后的Token流进行语法分析。这一步再次检查这些关键字组合是否符合Java语言规范(比如紧跟在if后面的是否有布尔判断表达式),词法分析的结果形成一棵符合Java语言规范的抽象语法树。3.语义分析语义分析是通过语义分析器进行的。语音分析主要是将一些难的复杂的语法转化为更简单的语法,得到最简单的语法(比如将foreach转化为for循环,带注解等),最后经过注解形成一个抽象的语法树,这个语法树就是更接近目标语言的语法规则。4.生成字节码通过字节码生成器生成字节码,根据注释语法抽象树生成字节码,即将一种数据结构转换为另一种数据结构。最后生成我们想要的.class文件。使用十六进制查看类文件的内容。我只使用记事本++。选择Text→Plug-in→Converter→ASCII->HEXclass文件开头是CAFEBABE。如果想了解这里的十六进制字节码的含义,可以参考https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.htmljavap查看类文件内容javap是一个Java类文件可以反编译(即反编译javac编译的文件)的decomposer,还可以查看java编译器生成的字节码。新建一个User.java源文件,用javac编译生成User.classs。packagecom.tian.demo.test;publicclassUser{privateintage=22;privateStringname="tian";publicintaddAge(){returnage=age+1;}publicstaticvoidmain(String[]args){}}使用javap指令javap-vUser.class>log.txt打开log.txtClassfile/D:/workspace/new/demo/src/main/java/com/tian/demo/test/User.classLastmodified2020-11-5;size441bytesMD5checksum2fa72d3f53bd9f138e0bfae82aba67e3Compiledfrom"User.java"publicclasscom.tian.demo.test.Userminorversion:0majorversion:52flags:ACC_PUBLIC,ACC_SUPERConstantpool:#1=Methodref#6.#21//java/lang/Object."":()V#2=Fieldref#5.#22//com/tian/demo/test/User.age:I#3=String#23//tian#4=Fieldref#5.#24//com/tian/demo/test/User.name:Ljava/lang/String;#5=Class#25//com/tian/demo/test/User#6=Class#26//java/lang/Object#7=Utf8age#8=Utf8I#9=Utf8name#10=Utf8Ljava/lang/字符串;#11=Utf8#12=Utf8()V#13=Utf8Code#14=Utf8LineNumberTable#15=Utf8addAge#16=Utf8()I#17=Utf8main#18=Utf8([Ljava/lang/String;)V#19=Utf8搜rceFile#20=Utf8User.java#21=NameAndType#11:#12//"":()V#22=NameAndType#7:#8//age:I#23=Utf8tian#24=NameAndType#9:#10//name:Ljava/lang/String;#25=Utf8com/tian/demo/test/User#26=Utf8java/lang/Object{publiccom.tian.demo.test.User();描述符:()vflags:ACC_PUBLICCode:stack=2,locals=1,args_size=10:aload_01:invokespecial#1//Methodjava/lang/Object."":()V4:aload_05:bipush227:putfield#2//字段:I10:aload_011:ldc#3//Stringtian13:putfield#4//Fieldname:Ljava/lang/String;16:returnLineNumberTable:line3:0line4:4line5:10publicintaddAge();descriptor:()Iflags:ACC_PUBLICCode:stack=3,locals=1,args_size=10:aload_01:aload_02:getfield#2//字段:I5:iconst_16:iadd7:dup_x18:putfield#2//字段:I11:ireturnLineNumberTable:line8:0publicstaticvoidmain(java.lang.String[]);描述符:([Ljava/lang/String;)Vflags:ACC_PUBLIC,ACC_STATICCode:stack=0,locals=1,args_size=10:returnLineNumberTable:line13:0}SourceFile:"User.java"幻数和类文件版本常量池访问标志类索引,父类索引,接口索引字段表集合方法表集合属性表集合然后JVM可以读取User.class文件进行解析等等以上一系列操作都是我们的Java文件到类文件。