(一)一次编译,到处运行,平台无关性。
1. Compile Once,Run Anyway如何实现?
首先,创建一个java类。代码如下:
package interview.javabasic;
/**
* java特性之Compile Once,Run Anyway
*
* @author kimtian
*/
public class CodeByteSample {
public static void main(String[] args) {
int i = 5, j = 5;
i++;
++j;
System.out.println(i);
System.out.println(j);
}
}
通常把java分为编译时和运行时。
(1) 编译时使用javac命令,编译的是java的源码,即将源码编译生成字节码,并存入对应的.class文件中。
编译时会检查语法、句法等错误。编译完成后会生成一个class文件,class文件保存我们java文件翻译成的二进制字节码,java类文件中的属性、方法,以及类中的常量信息都会被分别存储在.class文件中。
(2)然后再使用java XXX.class文件执行该程序。
注意:使用java执行时,要在包头路径下,并带上包路径。否则会报错找不到或无法加载主类。
没带上包路径报错:
所在目录层级不对,不在包头路径下:
正确使用方式及返回结果:
(3) javap 是jdk自带的反汇编器,可以查看java编译器为我们生成的字节码,通过比较字节码和源代码可以发现很多问题,一个很重要的作用是了解很多编译器内部工作的机制。
- javap -help 寻求帮助文档。
- javap -c 对代码进行反汇编
真正加载class文件去执行的java虚指令(字节码):
Compiled from "CodeByteSample.java" 表示从CodeByteSample.java编译而来。
public interview.javabasic.CodeByteSample(); 无参构造函数。
invokespecial #1 调用父类Object类方法super()。
return 退出构造函数
可以看出当我们不指定类的构造函数的时候,编译器会默认为我们生成一个不带参数的构造函数。
0: iconst_5 把常量5放入栈顶
1: istore_1 将栈顶元素放入局部变量1当中
2: iconst_5 把常量5放入栈顶
3: istore_2 将栈顶元素放入局部变量2当
4: iinc 1, 1 调用inc函数,将变量1加上1
7: iinc 2, 1 调用inc函数,将变量1加上1
10: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 获取PrintStream的静态域对象,将其压入栈顶
13: iload_1 将本地变量1(i)的值推送至栈顶
14: invokevirtual #3 // Method java/io/PrintStream.println:(I)V 调用PrintStream的prinltl去打印该值
17: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 获取PrintStream的静态域对象,将其压入栈顶
20: iload_2 将本地变量2(j)的值推送至栈顶
21: invokevirtual #3 // Method java/io/PrintStream.println:(I)V 调用PrintStream的prinltl去打印该值
24: return 退出整个方法
(4)编译过程
总结:
Java源码首先被编译成字节码,再由不同平台的JVM进行解析,Java语言在不同的平台上运行时不需要进行重新编译,java虚拟机在执行字节码的时候,把字节码转换成具体平台上的机器指令。
2.为什么JVM不直接将源码解析成机器码去执行
(1)每次执行都需要各种语法、句法、语义的检查。都要重新编译,重新分析,性能受到影响。
引入中间字节码,能够保证被编译成字节码后,多次执行程序不需要各种校验和补全。
(2)脱离java的束缚。由scala,ruby这些语言生成字节码,同样可以被JVM进行调用、执行,进而也能增加平台的兼容、扩展能力。这符合软件设计的中庸之道。
3.JAVA虚拟机
(1)Java虚拟机是一种抽象化的计算机,通过在实际的计算机上仿真、模拟各种计算机功能来实现的。JVM有自己完善的硬件架构,如处理器、堆、栈、寄存器等,还具有相应的指令系统。JVM屏蔽了与具体操作系统平台相关的信息,使得java程序只需生成在java虚拟机上运行的目标代码及字节码,就可以在多种平台上不加修改的运行。
所以一般情况下我们不需要知道虚拟机的运行原理,只需要专注写java代码。虚拟机可以屏蔽底层操作系统平台的不同,并且减少基于原生语言开发的复杂性。
JVM是一个内存中的虚拟机,意味着JVM的存储就是内存。所写的所有类、常量、变量、方法都在内存中,这决定着我们程序运行的是否健壮,是否高效。
(2)JVM由四部分组成:Class Loader、Runtime Data Area、Execution Engine、Native Interface
Class Loader:依据特定格式,加载class文件到内存;
Runtime Data Area:JVM内存空间结构模型。
Execution Engine:解释器,对命令进行解析,并提交到操作系统去执行。
Native Interface:融合不同开发语言的原生库为java所用。