一、什么是平台无关性
- Java语言是一款跨平台的语言,不管是在windows还是在Linux又或者是mac os,它都可以支持并在上边运行,秉持着一次编译,随处运行(Compile Once,Run Anywhere)的原则。
- Java程序可以编译成.class二进制文件,通过ClassLoader装载进系统,然后由各个平台的JVM(Java虚拟机)解析进行连接、初始化等操作,从而达到不改变程序代码实现各个平台运行的目的,这也是Java迅速风靡、建立庞大生态圈的一个重要原因。
二、平台无关性的实现
具体流程大概就是
- xxx.java文件进行编译生成xxx.class(JVM所能理解的字节码文件文件),这也是跨平台的基础
- 很多平台都有匹配JVM虚拟机,CLassLoader将.class文件装载进JVM
- JVM对.class文件进行解析,转换成特定平台的执行指令
我们举个小例子
package com.mtli.javabasic;
/**
* @Description:平台无关性测试
* @Author: Mt.Li
* @Create: 2020-04-25 08:55
*/
public class ByteCodeSample {
public static void main(String[] args) {
int i= 1,j=5;
// 故意写错,看javac信息
i++;
++j;
System.out.println(i);
System.out.println(j);
}
}
在idea的Terminal或者系统cmd中对java程序进行编译(我这里用的是idea)
Terminal用bash/内嵌cmd运行cd java_basic/src/com/mtli/javabasic
,然后javac ByteCodeSample.java
,我们来看看结果
编译不通过,我们对源码进行修改:
package com.mtli.javabasic;
/**
* @Description:
* @Author: Mt.Li
* @Create: 2020-04-25 08:55
*/
public class ByteCodeSample {
public static void main(String[] args) {
int i= 1,j=5;
// 故意报错,看javac信息
//i++++;
i++;
++j;
System.out.println(i);
System.out.println(j);
}
}
再次编译,发现.java下边生成了.class文件
然后在javabasic目录下执行该class文件:java com.mtli.javabasic.ByteCodeSample
,结果如下:
有的人会问为什么.class文件点进去能看到源码呢?不应该是字节码文件吗?那是因为我们查看的时候,idea自动为我们进行反编译了,想看字节码的可以去资源目录里面找到该文件,打开后:
为了更方便我们剖析,我们使用javap -c com/mtli/javabasic/ByteCodeSample
进行反汇编:
Compiled from "ByteCodeSample.java" // 由ByteCodeSample.java编译而来
public class com.mtli.javabasic.ByteCodeSample {
public com.mtli.javabasic.ByteCodeSample(); // 无参构造函数,下面的code就是无参构造执行的内容
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Obj // 执行super
ect."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: iconst_1 // 常量1放到栈顶
1: istore_1 // 将栈顶的值放到局部变量i中(出栈)
2: iconst_5 // 将5放到栈顶
3: istore_2 // 将栈顶的值放到j中
4: iinc 1, 1 // 变量1+1
7: iinc 2, 1 // 变量2+1
10: getstatic #2 // 获取静态域 // Field java/lang/System.out:Ljava/io/PrintStream;
13: iload_1 // 将变量1放到栈顶(入栈)
14: invokevirtual #3 // 打印 // Method java/io/PrintStream.println:(I)V
17: getstatic #2 // 获取静态域 // Field java/lang/System.out:Ljava/io/PrintStream;
20: iload_2
21: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
24: return
}
回到正题,我们接下来将.class文件放到服务器上(linux)上面执行(linux得预先安装linux版本的jdk1.8x,我的是win下1.8x下编译的,服务器也要装1.8x,只要是1.8就行),首先我们需要创建好包,也就是创建对应的文件夹了
我比较懒,直接将src放上去了,然后回到命令行,cd /src
——>java com.mtli.javabasic.ByteCodeSample
,可以看到
我们源码是没有改动的,通过linux平台匹配的JVM就可以直接运行,可以说Java语言移植性是很不错的。
三、为什么JVM不直接将源码解析成机器码去执行
讲到这里,可能有的朋友会有这个疑惑,编译过程是比较繁琐的,需要进行各种检查,如果把这个事儿都交给JVM干,别个不得累死,直接把它看得懂的文件给它不更好吗。再说了,统一成字节码文件给JVM也有利于其他语言的兄弟移植啊。总结成两点:
- 避免繁琐的准备工作即每次执行都要各种检查
- 兼容性:也可以将别的语言解析成字节码