緣由就是字節碼,因爲不一樣的平臺編譯出來的機器碼0,1是不一樣的,java採用不直接編譯成機器碼(0,1)而是把他們編譯成字節碼。再由不一樣平臺上的JVM翻譯成對應平臺的機器碼(0,1)。現在,JVM也再也不只支持Java,由此衍生出了許多基於JVM的編程語言,如Groovy, Scala, Koltin等等。java
而字節碼命令所能提供的語義描述能力是要明顯強於Java自己的,因此有其餘一些一樣基於JVM的語言能提供許多Java所不支持的語言特性。編程
2、如何看.class文件的十六進制文件格式數組
用NotePad++直接打開顯示亂碼,咱們點開插件--插件管理。而後選擇上面標記的地方,而後點安裝。編程語言
安裝後從新打開.class文件,咱們發現仍是亂碼。點插件---HEX-Editor而後選擇View in HEX就能夠了ide
3、如何看字節碼文件,咱們可使用JDK提供的javap命令進行反編譯.class,具體用法以下。實際過程當中咱們直接利用IDE看就能夠了,例如IDEA直接選中java文件而後View---Show Bytecode就能夠看了(首先這個java文件得編譯,沒有編譯在Build中選從新編譯一下便可)ui
四:解讀字節碼this
package test; public class ReadBytecode { private boolean bo; private byte b; private char c; private short s; private float f; private double d; private int i; private long l; private Integer id; private String name; public void m1(){} public int m2(){ return 1; } public static void main(String[] args) { } } //字節碼 // class version 52.0 (52) // access flags 0x21 public class test/ReadBytecode { // compiled from: ReadBytecode.java // access flags 0x2 private Z bo // access flags 0x2 private B b // access flags 0x2 private C c // access flags 0x2 private S s // access flags 0x2 private F f // access flags 0x2 private D d // access flags 0x2 private I i // access flags 0x2 private J l // access flags 0x2 private Ljava/lang/Integer; id // access flags 0x2 private Ljava/lang/String; name // access flags 0x1 public <init>()V L0 LINENUMBER 8 L0 ALOAD 0 INVOKESPECIAL java/lang/Object.<init> ()V RETURN L1 LOCALVARIABLE this Ltest/ReadBytecode; L0 L1 0 MAXSTACK = 1 MAXLOCALS = 1 // access flags 0x1 public m1()V L0 LINENUMBER 19 L0 RETURN L1 LOCALVARIABLE this Ltest/ReadBytecode; L0 L1 0 MAXSTACK = 0 MAXLOCALS = 1 // access flags 0x1 public m2()I L0 LINENUMBER 21 L0 ICONST_1 IRETURN L1 LOCALVARIABLE this Ltest/ReadBytecode; L0 L1 0 MAXSTACK = 1 MAXLOCALS = 1 // access flags 0x9 public static main([Ljava/lang/String;)V // parameter args L0 LINENUMBER 25 L0 RETURN L1 LOCALVARIABLE args [Ljava/lang/String; L0 L1 0 MAXSTACK = 0 MAXLOCALS = 1 }
分析上面代碼咱們能夠獲得,52是版本號表明java8,由於java的版本號從45開始,除1.0和1.1都是使用45.x外,之後每升一個大版本,版本號加一。也就是說,編譯生成該class文件的jdk版本爲1.8.0。spa
大多數的基本數據類型用其首字母大寫 (除了long 類型用J,boolean用Z),引用類型用L表示而且以;結束。變量名會在後面寫。方法返回值類型中多了V(void)。init方法返回值爲V。init方法除了執行構造方法,還會進行初值的賦值等例如咱們上面給一個變量賦一個初值,那麼動做也是在這裏執行的。從主方法的參數咱們能夠看出數組用[lxx類型. 二位數組[[L依次類推。.net
分析上面的構造方法:插件
L0 L1是方法中字節碼行號依次類推L2 L3。。。
LINENUMBER 字節碼偏移量,即字節碼和源碼行數的偏移量。在發生異常的時候能夠找到對應的源碼位置(能夠經過修改編譯參數去掉,去掉的話出現異常編譯器不會找到源代碼異常發生處)
ALOAD 0 將引用型變量壓入棧頂,此處就是把調用的方法壓入棧頂
INVOKESPECIAL 調用後面的方法
RETURN 彈棧
LOCALVARIABLE 幀棧中定義的局部變量與源碼定義的變量之間的關係。若是沒有這項信息(經過修改編譯參數能夠去掉),別人引用這個方法的時候沒法獲取參數名稱,取而代之的是arg0,arg1這樣的佔位符。
每一個實例方法中,都會有一個默認參數this. LOCALVARIABLE 後第一個參數爲參數名稱,第二個參數類型,第三第四個參數爲這個參數在字節碼中的可見行範圍,最後一個參數表示這個參數在這個楨棧中的位置,例如上面的0。
MAXSTACK 最大操做數棧,JVM會根據這個值來分配棧楨中的操做棧深度,上面爲1
MAXLOCALS 局部變量所需的存儲空間,單位爲Slot。Slot爲JVM爲局部變量分配內存的最小單位,大小爲4個字節。這裏的局部變量包括方法參數(包括隱藏的this)、方法內的局部變量、try--catch中catch()定義的參數。注意MAXLOCALS 並不必定等於全部局部變量的Slot和,由於局部變量中的Slot能夠重用。
分析下面的m1()方法。很簡單由於沒有執行語句因此直接彈棧,MAXSTACK爲0;
分析m2()方法:
ICONST_1 將int型1推送到棧頂
IRETURN 從當前方法返回int型數據
分析主方法:因爲是static 方法因此不會有this
更多字節碼指令查詢:https://blog.csdn.net/zqz_zqz/article/details/79484757