熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Java核心技術 >> 正文

高級應用——Java動態的程序設計

2013-11-23 19:35:37  來源: Java核心技術 

  類和類的裝載
  
  我們來看一下類以及它們被JVM裝載的時候做了些什麼?
  
  在這個新的有關動態的Java編程特征的系列文章中將會看到在正在執行的Java應用程序的背後發生了些什麼企業級Java專家Dennis Sosnoski給出了Java二進制格式和發生在JVM內部的類中的事情遵循這條路線他介紹正在裝載的類所影響的范圍(從正在運行的一簡單的Java應用程序所必須的大量的類到在JEE和類似的復雜的框架結構中類裝載器沖突所可能導致的問題)
  
  這篇文章揭示了Java動態編程這組主題所包含的一系列的新的知識這些主題包括從Java二進制類文件格式的結構到使用反射訪問運行時的元數據以及所有的在運行時編輯和構造新的類的方法貫穿這個材料的全部基本路線是Java平台的編程思想是比用其它直接編譯成本地代碼的語言更加動態的工作如果你理解了這些動態的特征你就可用Java語言做一些用其它的主流的編程語言所不能做的事情
  
  在這篇文章中我介紹了位於Java平台的動態特征之下的一些基本概念這些概念圍繞用於描述Java類的二進制格式包括類被裝載進JVM(Java虛擬機)時所發生的事情這篇文件不僅為理解這個系列主題的其它文章提供基礎同時也演示了一些非常實際的在Java平台上工作的開發人員所關心的事情
  
  一個類的二進制形式
  
  用Java語言的開發人員通常不必關心通過編譯器運行他們的源代碼時所發生的一些細節問題在這個系列主題中我會介紹許多有關從源代碼到可執行的程序這個過程的背後細節因此我們先來看一下編譯器所產生的二進制類
  
  二進制類的格式實際上是被JVM(Java虛擬機)規范定義的正常的類的描述是一個編譯器利用Java語言的源代碼生成的並且通常被保存在一以class為擴展名的文件中但是這些特征都不是本質的其它的一些編程語言已經被開發使用Java的二進制類的格式並且因為一些目的新的類的描述被創建並且被直接裝載進一個正在執行的JVM中但是JVM所關心的重要的不是這些源代碼或它是怎樣被存儲的而是這個格式自身
  
  因此先來這種類格式看上去象什麼呢?下面(List )列出了一個非常短的類的源代碼緊跟著是用編譯器輸出的這個類文件的一部分十六進制的顯示
  
  List Hellojava的源代碼和(部分)二進制表示
  public class Hello
  {
  public static void main(String[] args) {
  Systemoutprintln(Hello World!);
  }
  }
  
  : cafe babe e a a c 
  : d e fa  
  : c e e  <init>()
  : f d e VCodemain
  : b ca f c e f ([Ljava/lang/S
  : e b c  tring;)V
  : c d cc fc  Hello W
  : f c c  orld!
  : cc f a fc e Hellojava/lan
  : f f a a f g/Objectjava/
  a: c e f d f lang/Systemou
  
  
  二進制的內部
  
  List中所顯示的二進制類的表示的第一件事情是標識Java二進制類的格式的café babe簽名這個簽名只是一種確認實際請求的Java類的格式的一個實例的數據塊的簡易方法每個Java的二進制類即使在不同的文件系統上也需要用這四個字節開始
  
  數據的其它部分不是很有趣跟在簽名後面是一對類格式的版本號(在這個例子中javac編譯生成的時候會產生次版本為主版本為十六進制的形式是xe的版本號)然後是常量池中的條目的計數跟在條目計數(在這個例子中是xa)後面的是實際的常量池數據這是保存所有類定義所使用的常量的地方它包括類和方法的名字簽名以及字符串(這些字符串是你能夠認可的在十六制的存放處的正確性的文本解釋)以及連同在一起的各種二進制值
  
  在常量池中項目是可變長度的每個項目的第一個字節標識了項目的類型和它應該怎樣被解碼我不打算對這些內容做詳細介紹如果你有興趣以實際的JVM規范開始這裡有許多有用參考關鍵點是常量池包含了所有的對其它類和這個類所使用的方法的引用還有這個類自身以及它的方法的實際定義盡管平均值可能會少一些但是常量池的大小很容易的超過二進制類的在小的一半或更多
  
  跟在常量池後面是幾個引用常量池條目的項目它們是類本身它的超類以及接口這些項目的後面是有關字段和方法的信息這些信息是做為復合結構來描述自己的對於方法的可執行代碼以代碼屬性(code attributes)的形式被包含在方法的定義中這種代碼是JVM的指令形式通常叫做字節碼(bytecode)這是下一節的主題之一
  
  在Java類的格式中屬性(Attributes)用來做為幾種定義的用途包括已經提到的字節碼(bytecode)用於字段的常量值異常處理以及調試信息但是屬性(Attributes)不只有這些可能的用途從一開始JVM規范要求JVMs(Java虛擬機)忽略未知類型的屬性這種要求對於屬性的使用提供了靈活性使得它在將來能夠服務於其它的用途例如提供與用戶類一起工作的框架所需要的元信息這是一種Java源於C#語言所廣泛使用的方法不幸的是no hook have yet been provided for making of this flexibility at the user level
  
  字節碼和堆棧
  
  組成類文件的可執行部分的字節碼是適應特定類型計算機(JVM)是的實際的機器碼這所以叫做虛擬機是因為它是用軟件來設計實現的而不是硬件每個運行在JVM上的應用程序都是建立在這種機器的一種實現
  
  虛擬機實際上相當的簡單它使用堆棧結構這就意味著它們在被使用之前指令操作要被裝載進一個內部的堆棧指令集包括所有的一般的算術運算和邏輯操作還有有條件和無條轉移裝載/存儲調用/返回堆棧的維護以及幾種特殊的指令類型包括立即數的一些指令被直接編碼進指令另外一些直接從常量池引用值
  
  雖然虛擬機是簡單的但執行起來卻不是這樣的第一代JVM基本上是虛擬機的字節碼的解析器相對而言比較簡單但卻遇到嚴重的性能問題———解析代碼總是要比執行本地代碼花費更長的時間為了減少這些性能問題第二代JVM添加了即時(JIT)翻譯JIT技術是在Java字節碼第一次執行之前把它編譯成本地代碼從而為重復執行提供了更好的性能當前的JVM做的更好它使用相應的技術來監控程序的執行並且選擇性使使用代碼得到優化
  
  裝載類
  
  把源代碼編譯成本地代碼的語言(如C和C++)在源代碼被編譯之後通常需要鏈接這樣的步驟這種鏈接過程把獨立編譯的源文件連同共享類庫的代碼合並到一起從而形成一個可執行的程序Java語言是不同的使用Java語言編譯器生成的類文件一般情況下單獨保存的直到它們裝載進一個JVM為止即使是建立一個JAR文件也不會改變這種情況———JAR文件只是類文件的一個容器
  
  優於一個分開的步驟JVM把類裝載進內存的時候鏈接類成為JVM所要執行的工作的一部分這樣就可以在初始化裝載的時候增加一些系統開銷但是也為Java應用程序提供了高級的靈活性例如應用程序可以使用直到運行時才知道的實際實現的接口來編寫這種後期綁定(late binding)的方法來裝配一個應用程序在Java平台中被廣泛使用servlets就是一個普通的例子
  
  對於裝載類的規則在JVM規范的細節中被清楚的說明了基本原則是類只有在需要的時候才被裝載(或者至少是顯示的裝載JVM的這種方法在實際裝載過程中有一些靈活性但是必需保持一個固定的類初始化的順序)每個被裝載的類可以有其它的它所依賴的類因此裝載過程是遞歸的在Listing 中的類顯示了這種遞歸裝載是怎樣工作的這個Demo類包含了一個簡單的創建Greeter類的一個實例並且調用這個類的greet方法的main方法Greeter類的構造器創建了一個Message的實例然後它在greet方法中使用這個Message實例
  
  Listing 用於類裝載演示的源碼
  public class Demo
  {
  public static void main(String[] args) {
  Systemoutprintln(**beginning execution**);
  Greeter greeter = new Greeter();
  Systemoutprintln(**created Greeter**);
  greetergreet();
  }
  }
  
  public class Greeter
  {
  private static Message s_message = new Message(Hello World!);
  
  public void greet() {
  s_messageprint(Systemout);
  }
  }
  
  public class Message
  {
  private String m_text;
  
  public Message(String text) {
  m_text = text;
  }
  
  public void print(javaioPrintStream ps) {
  psprintln(m_text);
  }
  }
  
  設置java命令的命令行參數為verbose:class這樣就可打印類裝載過程的軌跡Listing 顯示了使用這個參數的來運行Listing 時的部分輸出
  [Opened /usr/java/jsdk/jre/lib/rtjar]
  [Opened /us
From:http://tw.wingwit.com/Article/program/Java/hx/201311/27138.html
    推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.