新家地址
??我们打开任何一本Java学习的书籍,都会告诉我们用记事本或者其他的文本编辑器写一个简单的小程序,然后javac 类名,进行编译,会在同一文件下生成一个同名的.calss文件;我们这里先怒写一个HelloWorld.java,代码如下:
?
class="java">public class HelloWorld { public static void main(String[] args){ System.out.println("Hello World!"); } }
?
?.class文件反序列化,代码如下:
?
package com.tao.study.one; public class HelloWorld { public HelloWorld() { } public static void main(String[] args) { System.out.println("Hello World!"); } }
?
我们用cmd命令打开我们编译后的.class文件,cmd的命令为:javap -v 类名,HelloWorld.class的内部代码如下:
Classfile /E:/DataBase/Idea/JavaStudy/out/production/JavaStudy/com/tao/study/one/HelloWorld.class Last modified 2017-12-3; size 570 bytes MD5 checksum e34e8917fa1c76751b4ea9a2f491d17a Compiled from "HelloWorld.java" public class com.tao.study.one.HelloWorld minor version: 0 //副版本号 major version: 52 //编译器版本号 == jdk1.8 flags: ACC_PUBLIC, ACC_SUPER //常量池 Constant pool: #1 = Methodref #6.#20 // java/lang/Object."<init>":()V #2 = Fieldref #21.#22 // java/lang/System.out:Ljava/io/PrintStream; #3 = String #23 // Hello World! #4 = Methodref #24.#25 // java/io/PrintStream.println:(Ljava/lang/String;)V #5 = Class #26 // com/tao/study/one/HelloWorld #6 = Class #27 // java/lang/Object #7 = Utf8 <init> #8 = Utf8 ()V #9 = Utf8 Code #10 = Utf8 LineNumberTable #11 = Utf8 LocalVariableTable #12 = Utf8 this #13 = Utf8 Lcom/tao/study/one/HelloWorld; #14 = Utf8 main #15 = Utf8 ([Ljava/lang/String;)V #16 = Utf8 args #17 = Utf8 [Ljava/lang/String; #18 = Utf8 SourceFile #19 = Utf8 HelloWorld.java #20 = NameAndType #7:#8 // "<init>":()V #21 = Class #28 // java/lang/System #22 = NameAndType #29:#30 // out:Ljava/io/PrintStream; #23 = Utf8 Hello World! #24 = Class #31 // java/io/PrintStream #25 = NameAndType #32:#33 // println:(Ljava/lang/String;)V #26 = Utf8 com/tao/study/one/HelloWorld #27 = Utf8 java/lang/Object #28 = Utf8 java/lang/System #29 = Utf8 out #30 = Utf8 Ljava/io/PrintStream; #31 = Utf8 java/io/PrintStream #32 = Utf8 println #33 = Utf8 (Ljava/lang/String;)V { public com.tao.study.one.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 6: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcom/tao/study/one/HelloWorld; 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 World! 5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return LineNumberTable: line 8: 0 line 9: 8 LocalVariableTable: Start Length Slot Name Signature 0 9 0 args [Ljava/lang/String; } SourceFile: "HelloWorld.java"
?
二、提出疑问?
??看到上面的结果,我们的第一感觉肯定是,woc,怎么这么神奇,这里面都发生了什么呢?那我们就要带着这些疑问去探究他们了,在百度上一顿操作之后,我们知道了javac是java自带的编译器,将java语言规范转化成JVM能够识别的字节码文件(.class文件);这是就更加懵了,甚至有点想跳过,一个过来人告诉你,不要这样子哦。下面我们来 总结一下这里面的问题,然后去探究一下java的世界到底是一个怎样的存在。
1、 什么是javac编译器?它又有什么作用?它又是怎样工作的呢?
2、 为什么要把字节码文件加载到JVM中呢?字节码文件又是如何加载到JVM中的?
3、 JVM是什么?它是如何工作的呢?
??看到这些疑问你是不是有点兴奋了,原来java可以学习的东西有这么多的啊,那我们就开始我们的探索之旅吧。问题要一个个的解决,我们就按顺序进行研究吧!
1.1 ?javac是java语言自带的一种编译器,我们都知道java有自己的语言规范,写错了一小处代码,整个项目都会无法运行,这就是规范的魅力,无规矩不成方圆,代码世界中同样如此,每一门语言都是一个王国,我们都是它的臣民;但是java语言不是机器语言,我们的机器是无法识别的,于是我们的java国王大手一挥创建了外交部,让javac负责这个部门,让它负责对外交流,但是javac这个小伙发现一个人的力量是有限的,要充分发挥自己的部长身份,于是他找到自己的得力干将JVM,让他负责与机器王国进行交流;每当国王发布新的命令的时候(.java文件),javac就将其编译成字节码文件,然后直接丢给自己的小弟JVM,自己只负责与国王(java类)对话交流;
1.2 ?Java的编译环节分为四个步骤,绝大部分的编译器也是这样设计的,四个步骤依次为:
monospace; font-size: 13px; padding: 2px 4px; color: #555555; background: #eeeeee; border-radius: 3px;">注释:
Token序列就是一组对应源码字符集合的单词序列,其实上就是一个枚举类型,内部定义了许多符合java语法规范并与源码字符集合相对应的枚举常量;
??这时我们肯定会非常好奇,java源码是如何转换成Token序列的呢?java的源码又是如何和已经生成的Token序列保持一个长久的对应关系呢?
??词法解析器在将源码翻译成Token序列之前,会先把这个源码字符集合转化为一个Name对象,每一个源码字符集合都是一个Name对象;Keywsords这个类(这里可以把它理解为一个工具类,名字不重要)会把所有的枚举常量转换为一个Name对象,然后将其存储在Name对象的内部类Table中,这时候Name对象就与Token序列建立了一个映射关系;每当我们传进来一个源码字符集合的Name对象的时候,词法解析器就会先找到Name类的内部类Table,在里面获取到对应的Token对象后,将源码字符集合与对应的Token对象的关系存储在Keywords类的Token key[]数组中;我们可以用一张图来表示这个过程,如下:
1.2.2 ?语法解析:
语法解析的作用就是把匹配得到的Token序列整合成一个语法树;
1.2.3 ?语义分析:
语义分析就是将语法分析产生的语法树进一步完善,例如给类 添加的默认构造函数,检查变量在使用前是否初始化,将一些常量进行合并处理,检查操作变量类型是否匹配,检查所有的操作语句是否可达,检查异常是否已经捕获或者抛出,解除java语法糖,等。
1.2.4 ?生成字节码:
遍历语法树,生成最终的字节码。
1、大家都知道Java是一门可以跨平台的语言,就是因为java源代码的编译结果字节码文件,而字节码文件是在JVM上面运行的,JVM是java技术的真正核心,俗话说得好,一个人对java理解的深度取决于他对JVM的理解深度,由此可以看出,JVM是我们的重中之重;
2、字节码文件加载到JVM中又是经历了一个怎样的过程呢,那我们就读一下下面的这一篇文章吧!
畅谈类加载的过程
?
3、java王国的国王想知道JVM这个小伙是怎么做的这么优秀的,谁先彻底研究清除,重重有赏,无数谋士纷纷前往,但又有几个真的了解了这个城府如此之深的小伙呢;但我们不能放弃,下面就让我们一起去剖析一下JVM到底是一个什么东西,他是如何做到外交部长交给他的任务的:
绑架JVM