计算机能识别的是机器指令码,简称机器码。机器码是二进制的,计算机可以直接识别,但与人类的语言差别太大,不容易被人理解和记忆。后来,就诞生了各种高级语言,人们用高级语言编写程序,然后通过把程序解释或编译成机器码。
比如python,就是一种解释型语言。Python程序源码不需要编译,可以直接从源代码运行程序。Python解释器将源代码转换为字节码,然后把编译好的字节码转发到Python虚拟机(PVM)中进行执行。
而C语言就是典型的编译型语言,需要先用编译器编译成机器码,比如我们通常用gcc来编译C语言程序:
$ gcc hello.c # 编译
$ ./a.out # 执行
hello world!
那Java是解释型语言还是编译型语言呢?
「Java是兼具编译型语言与解释型语言的特点的」。程序员写好Java程序后,需要先用javac编译成JVM可以使用的字节码class文件。然后JVM加载class文件,逐条解释执行。在运行过程中,部分热点代码会被即时编译器编译成机器码。
源代码到字节码
Java语言的源代码是.java为后缀的文件。当然现在有很多其它高级语言也架构在JVM上,比如groovy、kotlin等。源代码是给人看的,易于阅读、理解、维护。
源代码经过编译后得到字节码,字节码是给JVM用的,易于理解和识别。字节码是以.class为后缀,其格式是JVM的一套规划,字节码人类对照文档也是勉强能看懂的,只是相对Java代码来说要难以理解一些而已。
Java与Python不同,Python不需要编译字节码文件(当然,Python也提供了这种操作),编译是一个自动的过程,一般不会在意它的存在。而Java会先编译好字节码文件,这样JVM直接读字节码文件,可以节省加载模块的时间,提高效率。同时字节码的形式也增加了反向工程的难度,可以保护源代码(当然,也可以被反编译)。
熟悉JVM的小伙伴都知道,它有一个“类加载过程”,可以说是老八股文了,经常会被面试官问到。类加载过程其实就是指的JVM从读取一个class文件到准备好这个类,以及最后销毁的整个过程。
所以「class文件其实是以“类”为单位的,这跟java文件有一些不同」。如果我们在一个Java文件里面声明多个类,用Javac编译出来会发现有多个class文件。比如我们声明一个One.java文件:
public class One {
public class OneInner {}
private class OnePrivateInner {}
public static class OneStaticInner {}
private static class OneprivateStaticInner {}
}
class Two{}
用Javac编译后,会出现6个class文件
➜ $ ls
'One$OneInner.class' 'One$OneStaticInner.class' One.class Two.class
'One$OnePrivateInner.class' 'One$OneprivateStaticInner.class' One.java