《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位設置爲。第11位叫做ACC_SUPER標誌位

      對於定義的接口,JVM在將其編譯成class文件時,會將class文件的訪問標誌的第8位 設置爲 。第8位叫做ACC_INTERFACE標誌位

 bclass文件表示的類或者接口的訪問權限有public類型的和包package類型的。

      如果類或者接口被聲明爲public類型的,那麼,JVM將其編譯成class文件時,會將class文件的訪問標誌的第16位設置爲。第16位叫做ACC_PUBLIC標誌符

 c. 類是否爲抽象類型的,即我們定義的類有沒有被abstract關鍵字修飾,即我們定義的類是否爲抽象類。

      如果我們形如:

[java] view plain copy print?

  1. public  abstract  class MyClass{......}   

定義某個類時,JVM將它編譯成class文件的時候,會將class文件的訪問標誌的第7位設置爲。第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 查看訪問標誌。


[java] view plain copy print?

  1. package com.louis.jvm;  

  2.   

  3. public class Simple {  

  4.   

  5. }  

使用UltraEdit查看編譯成的class文件,如下圖所示:


上述的圖中×××部分表示的是常量池部分,具體爲什麼是常量池部分不是本文的重點,有興趣的讀者可以參考我的《Java虛擬機原理圖解》系列關於常量池的博客,你就可以很輕鬆地識別常量它們了。

常量池後面緊跟着就是訪問標誌,它的十六進制值爲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,將結果重定向到文件中,然後查看文件)


 

3. 類索引(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文件所表示的是哪一個類。



3. 父類索引(super_class)是什麼?

    Java支持單繼承模式,除了java.lang.Object 類除外,每一個類都會有且只有一個父類。class文件中緊接着類索引(this_class)之後的兩個字節區域表示父類索引,跟類索引一樣,父類索引這兩個字節中的值指向了常量池中的某個常量池項CONSTANT_Class_info,表示該class表示的類是繼承自哪一個類。




4. 接口索引集合(interfaces)是什麼?

      一個類可以不實現任何接口,也可以實現很多個接口,爲了表示當前類實現的接口信息,class文件使用瞭如下結構體描述某個類的接口實現信息:


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


舉例:

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

      

[java] view plain copy print?

  1. /** 

  2.  * Worker 接口類 

  3.  * @author luan louis 

  4.  */  

  5. public interface Worker{  

  6.       

  7.     public void work();  

  8.   

  9. }  

[java] view plain copy print?

  1. package com.louis.jvm;  

  2.   

  3. public class Programmer implements Worker {  

  4.   

  5.     @Override  

  6.     public void work() {  

  7.         System.out.println("I'm Programmer,Just coding....");  

  8.     }  

  9. }  


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章