一、什麼是平臺無關性
- 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也有利於其他語言的兄弟移植啊。總結成兩點:
- 避免繁瑣的準備工作即每次執行都要各種檢查
- 兼容性:也可以將別的語言解析成字節碼