1. 程式人生 > >《Java虛擬機器原理圖解》1.3、class檔案中的訪問標誌、類索引、父類索引、介面索引集合

《Java虛擬機器原理圖解》1.3、class檔案中的訪問標誌、類索引、父類索引、介面索引集合

講完了class檔案中的常量池,我們就相當於克服了class檔案中最麻煩的模組了。現在,我們來看一下class檔案中緊接著常量池後面的幾個東西:訪問標誌、類索引、父類索引、介面索引集合

  1. 訪問標誌、類索引、父類索引、介面索引集合 在class檔案中的位置
    這裡寫圖片描述

  2. 訪問標誌(access_flags)能夠表示什麼?
    訪問標誌(access_flags)緊接著常量池後,佔有兩個位元組,總共16位,如下圖所示:

這裡寫圖片描述

當JVM在編譯某個類或者介面的原始碼時,JVM會解析出這個類或者介面的訪問標誌資訊,然後,將這些標誌設定到訪問標誌(access_flags)這16個位上。JVM會考慮如下設定如下訪問表示資訊:
a. 我們知道,每個定義的類或者介面都會生成class檔案(這裡也包括內部類,在某個類中定義的靜態內部類也會單獨生成一個class檔案)。
對於定義的類,JVM在將其編譯成class檔案時,會將class檔案的訪問標誌的第11位設定為1 。第11位叫做ACC_SUPER標誌位;
對於定義的介面,JVM在將其編譯成class檔案時,會將class檔案的訪問標誌的第8位 設定為 1 。第8位叫做ACC_INTERFACE標誌位;
b. class檔案表示的類或者介面的訪問許可權有public型別的和包package型別的。
如果類或者介面被宣告為public型別的,那麼,JVM將其編譯成class檔案時,會將class檔案的訪問標誌的第16位設定為1 。第16位叫做ACC_PUBLIC標誌符;
c. 類是否為抽象型別的,即我們定義的類有沒有被abstract關鍵字修飾,即我們定義的類是否為抽象類。
如果我們形如:
public abstract class MyClass{……}
定義某個類時,JVM將它編譯成class檔案的時候,會將class檔案的訪問標誌的第7位設定為1 。第7位叫做ACC_ABSTRACT標誌位。 另外值得注意的是,對於定義的介面,JVM在編譯介面的時候也會對class檔案的訪問標誌上的ACC_ABSTRACT標誌位設定為 1;
d. 該類是否被聲明瞭final型別,即表示該類不能被繼承。
此時JVM會在編譯class檔案的過程中,會將class檔案的訪問標誌的第12位設定為 1 。第12位叫做ACC_FINAL標誌位;
e.如果我們這個class檔案不是JVM通過java原始碼檔案編譯而成的,而是使用者自己通過class檔案的組織規則生成的,那麼,一般會對class檔案的訪問標誌第4位設定為 1 。通過JVM編譯原始碼產生的class檔案此標誌位為 0,第4位叫做ACC_SYNTHETIC標誌位;
f. 列舉類,對於定義的列舉類如:public enum EnumTest{….},JVM也會對此列舉類編譯成class檔案,這時,對於這樣的class檔案,JVM會對訪問標誌第2位設定為 1 ,以表示它是列舉類。第2位叫做ACC_ENUM標誌位;
g. 註解類,對於定義的註解類如:public @interface{…..},JVM會對此註解類編譯成class檔案,對於這樣的class檔案,JVM會將訪問標誌第3位設定為1,以表示這是個註解類,第3位叫做ACC_ANNOTATION標誌位。
當JVM確定了上述標誌位的值後,就可以確定訪問標誌(access_flags)的值了。實際上JVM上述標誌會根據上述確定的標誌位的值,對這些標誌位的值取或,便得到了訪問標誌(access_flags)。如下圖所示

這裡寫圖片描述

舉例:定義一個最簡單的類Simple.java,使用編譯器編譯成class檔案,然後觀察class檔案中的訪問標誌的值,以及使用javap -v Simple 檢視訪問標誌。


package com.louis.jvm;  

public class Simple {  

}

使用UltraEdit檢視編譯成的class檔案,如下圖所示:
這裡寫圖片描述

常量池後面緊跟著就是訪問標誌,它的十六進位制值為0x0021,二進位制的值為:00000000 00100001,由二進位制的1的位數可以得出第11、16位為1,分別對應ACC_SUPER標誌位和ACC_PUBLIC標誌位。
也可以通過一下運算:
0x0021 = 0x0001 | 0x0020, 即: 訪問標誌表示的標誌是ACC_PUBLIC + ACC_SUPER
為了驗證我們的運算,使用javap -v Simple檢視反編譯資訊如下:(小技巧:使用javap -v Simple指令的結果展示在命令提示符下顯示不友好,一般我是使用javap -v Simple > temp.txt,將結果重定向到檔案中,然後檢視檔案)
這裡寫圖片描述

  1. 類索引(this_class)是什麼?
    我們知道一般情況下一個Java類原始檔經過JVM編譯會生成一個class檔案,也有可能一個Java類原始檔中定義了其他類或者內部類,這樣編譯出來的class檔案就不止一個,但每一個class檔案表示某一個類,至於這個class表示哪一個類,便可以通過 類索引 這個資料項來確定。JVM通過類的完全限定名確定是某一個類。
    類索引的作用,就是為了指出class檔案所描述的這個類叫什麼名字。
    類索引緊接著訪問標誌的後面,佔有兩個位元組,在這兩個位元組中儲存的值是一個指向常量池的一個索引,該索引指向的是CONSTANT_Class_info常量池項
    這裡寫圖片描述

以上面定義的Simple.class 為例,如下圖所示,檢視他的類索引在什麼位置和取什麼值。
這裡寫圖片描述

由上可知,它的類索引值為0x0001,那麼,它指向了常量池中的第一個常量池項,那我們再看一下常量池中的資訊。使用javap -v Simple,常量池中有以下資訊:

這裡寫圖片描述

可以看到常量池中的第一項是CONSTANT_Class_info項,它表示一個”com/louis/jvm/Simple”的類名。即類索引是告訴我們這個class檔案所表示的是哪一個類。
這裡寫圖片描述

  1. 父類索引(super_class)是什麼?
    Java支援單繼承模式,除了java.lang.Object 類除外,每一個類都會有且只有一個父類。class檔案中緊接著類索引(this_class)之後的兩個位元組區域表示父類索引,跟類索引一樣,父類索引這兩個位元組中的值指向了常量池中的某個常量池項CONSTANT_Class_info,表示該class表示的類是繼承自哪一個類。

這裡寫圖片描述

  1. 介面索引集合(interfaces)是什麼?

一個類可以不實現任何介面,也可以實現很多個介面,為了表示當前類實現的介面資訊,class檔案使用瞭如下結構體描述某個類的介面實現資訊:
這裡寫圖片描述

由於類實現的介面數目不確定,所以介面索引集合的描述的前部分叫做介面計數器(interfaces_count),介面計數器佔用兩個位元組,其中的值表示著這個類實現了多少個介面,緊跟著介面計數器的部分就是介面索引部分了,每一個介面索引佔有兩個位元組,介面計數器的值代表著後面跟著的介面索引的個數。介面索引和類索引和父類索引一樣,其內的值儲存的是指向了常量池中的常量池項的索引,表示著這個介面的完全限定名。

舉例:
定義一個Worker介面,然後類Programmer實現這個Worker介面,然後我們觀察Programmer的介面索引集合是怎樣表示的。

/** 
 * Worker 介面類 
 * @author luan louis 
 */  
public interface Worker{  

    public void work();  

}  
package com.louis.jvm;  

public class Programmer implements Worker {  

    @Override  
    public void work() {  
        System.out.println("I'm Programmer,Just coding....");  
    }  
}  

這裡寫圖片描述