OpenJDK源碼研究筆記(八)-詳細解析如何讀取Java位元組碼檔案(.class)__Java

來源:互聯網
上載者:User

在上一篇OpenJDK源碼研究筆記(七)–Java字節碼檔案(.class)的結構中,我們大致瞭解了Java位元組碼檔案的結構。

本篇詳細地介紹了如何讀取.class檔案的大部分細節。

1.構造檔案 

 // 位元組碼檔案User.class  String userClass = "C:/User.class";  File file = new File(userClass);


2.構造輸入資料流

FileInputStream fin = new FileInputStream(file);DataInputStream in= new DataInputStream(new BufferedInputStream(fin));


3.讀取位元組碼檔案(.class)的內容

   3.1魔法數

//無符號4個位元組 int   magic = in.readInt();


   3.2次版本號碼

     //無符號2個位元組
     int   minor_version = in.readUnsignedShort();

   3.3主要版本號

 //無符號2個位元組      int  major_version = in.readUnsignedShort();


  3.4常量池的個數
      //無符號2個位元組
       int count = cr.readUnsignedShort();

  3.5常量池的內容

//CPInfo是常量池中的常量的類型,不能簡單地把常量池中的類型理解為String類型   CPInfo[]   pool = new CPInfo[count];  for (int i = 1; i < count; i++) {   // 常量池中常量的類型標記   int tag = cr.readUnsignedByte();   switch (tag) {   // 類或介面的符號引用   case CONSTANT_Class:    pool[i] = new CONSTANT_Class_info(this, cr);    break;   // 雙精確度浮點型字面量   case CONSTANT_Double:    pool[i] = new CONSTANT_Double_info(cr);    i++;    break;   // 欄位的符號引用   case CONSTANT_Fieldref:    pool[i] = new CONSTANT_Fieldref_info(this, cr);    break;   // 浮點型字面量   case CONSTANT_Float:    pool[i] = new CONSTANT_Float_info(cr);    break;   // 整型字面量   case CONSTANT_Integer:    pool[i] = new CONSTANT_Integer_info(cr);    break;   // 介面方法的引用   case CONSTANT_InterfaceMethodref:    pool[i] = new CONSTANT_InterfaceMethodref_info(this, cr);    break;   // 動態調用指令   case CONSTANT_InvokeDynamic:    pool[i] = new CONSTANT_InvokeDynamic_info(this, cr);    break;   // 長整型字面量   case CONSTANT_Long:    pool[i] = new CONSTANT_Long_info(cr);    i++;    break;   // 方法處理器   case CONSTANT_MethodHandle:    pool[i] = new CONSTANT_MethodHandle_info(this, cr);    break;   // 方法類型   case CONSTANT_MethodType:    pool[i] = new CONSTANT_MethodType_info(this, cr);    break;   // 方法引用   case CONSTANT_Methodref:    pool[i] = new CONSTANT_Methodref_info(this, cr);    break;   // 名字和類型   case CONSTANT_NameAndType:    pool[i] = new CONSTANT_NameAndType_info(this, cr);    break;   // 字串   case CONSTANT_String:    pool[i] = new CONSTANT_String_info(this, cr);    break;   // Utf8編碼的字串   case CONSTANT_Utf8:    pool[i] = new CONSTANT_Utf8_info(cr);    break;   // 不合法的類型   default:    throw new InvalidEntry(i, tag);   }  }


   構造細節

   舉一個例子,比如構造Double類型的常量結構CONSTANT_Double_info,就是在這個結構中儲存一個Double的值。

   此外,還維護了一些其它資訊,這個結構的位元組長度、類型標記。

  其它一些結構,類似。

   3.6訪問標識符

 //無符號2個位元組    int access_flag= cr.readUnsignedShort()


3.7這個類在常量池中的下標

//無符號2個位元組     int   this_class = in.readUnsignedShort();


3.8這個類的父類在常量池中的下標

  //無符號2個位元組     int   super_class = in.readUnsignedShort();


   3.9父介面的數量

    //無符號2個位元組

     int   int interfaces_count = cr.readUnsignedShort();

    3.10父介面的內容

interfaces = new int[interfaces_count];     for (int i = 0; i < interfaces_count; i++){           //父介面的類型,無符號2個位元組            interfaces[i] = cr.readUnsignedShort();    }


   3.11欄位的數量

//無符號2個位元組    int    int fields_count = cr.readUnsignedShort();


3.12欄位的內容

fields = new Field[fields_count];   for (int i = 0; i < fields_count; i++){            fields[i] = new Field(cr);   }//一個欄位Field有訪問標識符、名字、描述符、屬性      access_flags = cr.readUnsignedShort();        name_index = cr.readUnsignedShort();        descriptor = cr.readUnsignedShort()         attributes,封裝了欄位的更多詳細資料
 3.13方法的數量,無符號2個位元組
 int methods_count = cr.readUnsignedShort();

 

      3.14方法的內容

 methods = new Method[methods_count];   for (int i = 0; i < methods_count; i++)            methods[i] = new Method(cr);     //   一個方法Method有訪問標識符、名字、描述符、屬性        access_flags = cr.readUnsignedShort();        name_index = cr.readUnsignedShort();        descriptor = cr.readUnsignedShort()         attributes,封裝了方法的更多詳細資料  


       3.15屬性的數量

 //無符號2個位元組        int attrs_count = cr.readUnsignedShort();


       3.16屬性的內容

        

Attribute[]  attrs = new Attribute[attrs_count];        for (int i = 0; i < attrs_count; i++) {               attrs[i] =  readAttribute();         }/**  * 讀取一個屬性Attribute  */ public Attribute readAttribute() throws IOException {  //名字的索引  int name_index = readUnsignedShort();  //屬性內容的長度  int length = readInt();  byte[] data = new byte[length];  //讀取length個長度的位元組資訊到data中  readFully(data);  DataInputStream prev = in;  in = new DataInputStream(new ByteArrayInputStream(data));  try {   //根據名字的索引、位元組數組形式的內容構造一個Attribute對象   return attributeFactory.createAttribute(this, name_index, data);  } finally {   in = prev;  } }


4.總結

一個位元組碼檔案(.class)幾乎含有一個類或介面的所有資訊。

這些資訊雖然以二進位形式存在,但是它們的儲存結構仍然是有規律的。

按照儲存格式來依次讀取相應的位元組數目,就可以完整地解析一個位元組碼檔案。

5.挑戰極限

講完了位元組碼檔案的的結構和讀取位元組碼檔案的方法,有讀者可能會問,位元組碼檔案是怎麼產生的。

我們寫的Java程式都是文字格式設定的.java檔案,它們是如何轉換的呢。

其實,.java到.class的轉換過程,就是Java編譯器javac的主要功能。

Java編譯器javac比較有難度,剛剛開始研究,如有心得體會和研究成功,一定第一時間分享出來。

 

6.相關閱讀 OpenJDK源碼研究筆記(一)-參數檢查&拋出帶嚴重錯誤提示資訊的異常 OpenJDK源碼研究筆記(二)-Comparable和Comparator2個介面的作用和區別(一道經典的Java筆試面試題) OpenJDK源碼研究筆記(三)-RandomAccess等標記介面的作用

OpenJDK源碼研究筆記(四)-編寫和組織可複用的工具類和方法

OpenJDK源碼研究筆記(五)-緩衝Integer等類型的頻繁使用的資料和對象,大幅度提升效能(一道經典的Java筆試題)

OpenJDK源碼研究筆記(六)--觀察者模式工具類(Observer和Observable)和應用樣本

OpenJDK源碼研究筆記(七)–Java位元組碼檔案(.class)的結構

原文參見:http://FansUnion.cn/articles/2974

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.