Java類型信息(RTTI和反射)

  要想在IT領域站得住腳,必須得不斷地學習來強化本身,可是學過的技術不實踐很容易便被遺忘,因此一直都打算開個博客,來記錄本身學的知識,另外也能夠分享給有須要的人!java

  最近在學習反射,爲了更好地理解反射,就去查各類資料學習了java類型信息。程序員

  目錄數組

  • 前言
  • java類的加載和初始化
  • Class對象
  • java類型信息

  1、前言框架

  在瞭解java類型信息前,須要先了解咱們編寫的類在Java中是如何加載的,以及Class類的基本概念和做用,以方便咱們更好理解Java類型信息。學習

 

  2、類的加載和初始化 this

  2.1 類的加載編碼

  當程序要使用某個類時,若是該類還未被加載到內存中,則系統會經過加載,鏈接,初始化三步來實現對這個類的初始化。spa

  • 加載
    • 就是指class文件讀入內存,併爲之建立一個Class對象(下文會詳細介紹)
    • 任何類被使用時系統都會爲之創建一個Class對象。
  • 鏈接
    • 驗證 是否有正確的內部結構,並和其餘類協調一致
    • 準備 負責爲類的靜態成員分配內存,並設置默認初始化值
    • 解析 對類中的接口、類、方法、變量的符號引用進行解析並定位,解析成直接引用(符號引用就是編碼時用字符串表示某個變量、接口的位置,直接引用就是根據符號引用翻譯出來的地址)
  • 初始化
    • 初始化Java代碼和靜態Java代碼塊,即在內存中創建java內存模型

  2.2 類的加載時機翻譯

  • 第一次建立類的實例
  • 訪問類的靜態變量,或者爲靜態變量賦值
  • 調用類的靜態方法
  • 使用反射方法強制建立某個類或接口對應的java.lang.Class對象
  • 初始化某個類的子類
  • 直接使用java.exe命令來運行某個類

  2.3類加載器代理

  • Bootstrap ClassLoader 根類加載器
  • Extension ClassLoader 擴展類加載器
  • System ClassLoader 系統類加載器
  • Bootstrap ClassLoader 根類加載器
    •   也被稱爲引導類的加載器,負責Java核心類的加載
      •     好比System String等,在JDK中JRE的lib目錄下rt.jar文件中
  • Extension ClassLoader擴展類加載器
    •   負責JRE的擴展目錄中jar包的加載
      •     在JDK中JRE的lib目錄下ext目錄
  • System ClassLoader系統類加載器
    •   負責在JVM啓動時加載來自java命令的class文件,以及classpath環境變量所指定的jar包和類型

  2.4 雙親委派模型

  類加載器Java類如同其餘的Java類同樣,也是要由類加載器來加載的。除了啓動類根類加載器每一個類都有其父類加載器(父子關係有組合(不是繼承)來實現)。

  所謂雙親委派模型是指每次收到類加載請求時,先將請求委派給父類加載器完成(全部加載請求最終會委派到頂層的Bootstrap Class Loader加載器中),若是父類加載器沒法完成這個加載(該加載器的搜索範圍沒有找到對應的類),子類嘗試本身加載。

                                                                        

      雙親委派好處

      • 避免同一個類被加載屢次
      • 每一個加載 器只能加載本身範圍內的類

3、Class類

  Class類封裝一個對象和接口的運行時的狀態(即在這個類的對象中記錄每一個對象所屬的類,保存類的類型信息)。操做類對象在使用前會經歷上文所說的加載過程(加載、裂解和初始化),併爲之建立一個Class類的對象---Class對象,存放在操做類的.class文件中。這個Class類對象與操做類class是一一對應的,即當一個操做類被加載後,就不會重複加載。而經過該class對象,就能夠訪問對應的class。咱們能夠把Class理解爲一個類的標識對象,它至關因而一個類的銘牌。拿到一個Class,咱們就能夠找到對應的類。

注意,Class類沒有公有(public)構造方法,所以不能顯式地聲明一個Class對象,只在類加載時建立。

從Class對象中能夠得到的信息:

    • 獲取到class所包含的構造器
    • 獲取到class所包含的方法
    • 獲取到class所包含的成員變量
    • 獲取到class所包含的Annotation

 

另外,網上不少帖子說,一旦某個操做類的Class對象被載入內存,就用它來產生該操做類的全部對象!

獲取Class對象的三種方式:

    • 調用Object類的getClass()方法來獲得Class對象,如:
1 Dog dog; 2 Class class = dog.getClass();
    • 使用Class類中的靜態方法forName(string)得到與參數string對象的類的Class對象。例如
1 Class class = Class.forName("Dog");
    • 任何類都具有一個靜態的屬性(class屬性),可引用這個屬性得到對應的Class對象,如:
1 Class class = Dog.class;

Class類經常使用的方法

    • getName()
    • o 返回由 類對象表示的實體(類,接口,數組類,原始類型或空白)的名稱,做爲 String 。
    • newInstance()
    • o 建立由此 類對象表示的類的新實例。
    • getClassLoader
    • o 返回該類的類加載器

 

4、Java類型信息

  咱們知道,咱們的程序在運行時,某一個操做類能夠回被實例化多個對象,一個程序也可能擁有若干個不一樣操做類的多個對象,那麼Java如何在運行時識別對象所屬和類的信息的呢?答案是運行時類型信息(Run-Time Type Identification),RTTI使得你能夠在程序運行時發現和使用類型信息。

1、RTTI概要

  1. 類型信息RTTI:即對象和類的信息,例如類的名字、繼承的基類、實現的接口等。
  2. 類型信息的做用:程序員能夠在程序運行時發現和使用類型信息。
  3. RTTI真正含義:運行時,識別一個對象的類型。

2、RTTI的實現方式

  1. 傳統RTTI:即在編譯時已知道了全部的類型
  2. 反射機制:在程序運行時發現和使用類的信息

3、傳統RTTI的使用

 1 abstract class Shapes{  2     void draw(){  3         System.out.println(this+".draw()");  4  }  5     abstract public String toString();  6 }  7 
 8 class Circle extends Shapes{  9     public String toString(){ 10         return "Circle"; 11  } 12 } 13 class Triangle extends Shapes{ 14     public String toString(){ 15         return "Triangle"; 16  } 17 } 18 class Square extends Shapes{ 19     public String toString(){ 20         return "Square"; 21  } 22 } 23 class Test{ 24     public static List<Shapes>getList(){ 25         List<Shapes> list_aShapes = Arrays.asList(new Circle,new Square,new Triangle); 26  } 27 } 28 
29 public class Shape{ 30     public static void main(String args[]){ 31         List<Shapes> list_aShapes = Test.getList(); 32         for(Shapes shape:list_aShapes){ 33  shape.draw(); 34  } 35  } 36 }

  運行結果:Circle.draw()

  Square.draw()

  Triangle.draw()

  結果分析:

  上面代碼中,shape對象只是一個泛化引用,顯然一開始系統並不知道泛華引用的確切類型,咱們但願使用的是shape對象對應的確切類型,這是,就須要系統使用RTTI。上例,只是打印出泛化引用的全部類型。進一步解析,Circle,Square,Triangle三個類都繼承了抽象類Shape,現有一個List<Shape>的數組,存的是Circle,Square.Triangle的對象,當你拿出一個對象時,你只知道她是Shape類,但不知道它的具體類型。使用RTTI,能夠查詢到某個shape引用所指向對象的具體類型。

4、反射機制

  java反射的官方介紹是 反射機制是在運行中,對於任意一個類,都可以知道這個類的全部屬性和方法;對於任意一個對象,都可以調用它的任意一個方法和屬性。

  反射機制能作什麼?

    1. 在運行時判斷任意一個類所屬的類
    2. 在運行時構造任意一個類的對象
    3. 在運行時判斷任意一個所具備的成員變量和方法
    4. 在運行時調用任意一個對象的方法
    5. 生成動態代理  

   其實,咱們在學習框架時可以發現反射的普遍應用,經過反射和一個未知類型的對象打交道,Class類和java.lang.reflection類庫一塊兒對反射做技術支撐,該類庫包含Field,Method和Constructor類,這些類的對象由JVM啓動時建立,用以表示未知類裏對應的成員。

  關於反射的詳細內容以後的文章會介紹!

5、傳統RTTI和反射的共同點和區別

  區別:

    1、若是該類在編譯前就已知。也就是該類在classPath路徑下。這就是傳統RTTI。

    2、若是該類編譯器未知,也就是在程序運行時才知道的。這就是反射

  因此RTTI和反射的本質區別只是檢查一個類的.class文件的時機不一樣,反射:.class 文件是在編譯時不可得到的,因此在運行時打開和檢查未知類的.class文件從而變已知。RTTI:  .class 文件是在編譯時打開和檢查。

  相同點:都是基於Class類來執行的,均可以獲取類的類型信息。

6、總述

 其實你們會發現,相比於反射,咱們基本不會接觸到傳統RTTI機制,這是爲什麼呢?其實它的名字已經給出了答案,這是一個機制---傳統RTTI機制,其實在Java不少地方都依賴於這個機制來實現,好比說多態(當咱們把子類對象的引用賦給父類對象的狀況時,須要使用RTTI去識別該對象所屬的類),因此說這並非可有可有的。咱們也能夠在網上的不少文章看到,不少文章都是把RTTI分爲傳統RTTI和反射,以闡述java類型信息的詳情,這樣咱們便可以很好地學習反射,也搞清楚了Java在運行時對於對象的識別方法。

相關文章
相關標籤/搜索