反射---Java高級開發必須懂的

 
 
理解反射對學習Java框架有很大的幫助,如Spring框架的核心就是使用Java反射實現的,並且對作一些Java底層的操做會頗有幫助。 

1、Class類的使用java

        一、萬事萬物皆對象,(固然,基本數據類型,靜態成員不是面向對象(屬於類的)),因此咱們建立的每個類也都是對象,即類自己是java.lang.Class類的實例對象,可是這些對象都不須要new出來,由於java.lang.Class類的構造方法是私有的
        二、任何一個類都是Class類的實例對象,這個實例對象有三種表示方式:(咱們新建一個Student類)
              ① Class c1 = Student.class;//實際告訴咱們任何一個類都有一個隱含的靜態成員變量class(知道類名時用)
              ② Class c2 = stu.getClass();//已知該類的對象經過getClass方法(知道對象時用)  
              ③Class c3 = Class.forName("類的全名");//會有一個ClassNotFoundException異常
              官網解釋說:c1,c2表示了Student類的類類型()class type),萬事萬物皆對象,類也是對象,是Class類的實例對象,這個對象咱們成爲該類的類類型(有點亂,可是慢慢捋一下仍是能理解的)
             這裏有一點值得注意,當咱們執行System.out.println(c1==c2);語句,結果返回的是true,這是爲何呢?緣由是無論c1仍是c2都表明了Student類的類類型,一個類多是Class類的一個實例對象。
             咱們徹底能夠經過類的類類型建立該類的對象實例,即經過c1或c2建立Student的實例。
             Student stu = (Student)c1.newInstance();//前提是必需要有無參的構造方法,由於該語句會去調用其無參構造方法。該語句會拋出異常。
 
2、動態加載類
         一、編譯時加載類是靜態加載類,
                 new 建立對象是靜態加載類,在編譯時刻就須要加載全部可用使用到的類,若是有一個用不了,那麼整個文件都沒法經過編譯
         二、運行時加載類是動態加載類      
           Class c =  Class.forName("類的全名"),不只表示了類的類型,還表示了動態加載類,編譯不會報錯,在運行時纔會加載,使用接口標準能更方便動態加載類的實現。功能性的類儘可能使用動態加載,而不用靜態加載。
           不少軟件好比QQ,360的在線升級,並不須要從新編譯文件,只是動態的加載新的東西
            
3、獲取方法信息
       一、基本的數據類型,void關鍵字都存在類類型
      1. 1 Class c1 =int.class;//int的類類型
        2 Class c2 =String.class;//String類的類類型,能夠理解爲編譯生成的那個String.class字節碼文件, 3 //固然,這並非官方的說法
        4 Class c3 =double.class; 5 Class c4 =Double.class; 6 Class c5 =void.class;

         

       二、Class類的基本API操做         
           
 1 /**
 2  * 打印類的信息,包括類的成員函數,成員變量  3 * @param obj 該對象所屬類的信息  4 */
 5 publicstaticvoid printClassMessage(Object obj){  6 //要獲取類的信息,首先要獲取類的類類型
 7 Class c = obj.getClass();//傳遞的是哪一個子類的對象,c就是該子類的類類型  8 //獲取類的名稱
 9 System.out.println("累的名稱是:"+c.getName()); 10 
11 /*
12  * Method類,方法的對象 13 * 一個成員方法就是一個Method對象 14 * getMethods()方法獲取的是全部的public的函數,包括父類繼承而來的 15 * getDeclaredMethods()獲取的是多有該類本身聲明的方法,不問訪問權限 16 */
17 Method[] ms = c.getMethods();//c.getDeclaredMethods();
18 for(int i =0; i < ms.length; i++){ 19 //獲得方法的返回值類型的類類型
20 Class retrunType = ms[i].getReturnType(); 21 System.out.print(retrunType.getName()+" "); 22 //獲得方法的名稱
23 System.out.print(ms[i].getName()+"("); 24 //獲取的參數類型--->獲得的是參數列表的類型的類類型
25 Class[] paraTypes = ms[i].getParameterTypes(); 26 for(Class class1 : paraTypes){ 27 System.out.print(class1.getName()+","); 28 } 29 System.out.println(")"); 30 } 31 }  

           Class的API中還有不少其餘的方法,能夠獲得interface、Package、Annotation等不少信息,具體使用請參考幫助手冊,本文就不在詳細講解。特別注意的一點是,若是你想獲得一個類的信息,首先就要獲取該類的類類型。
框架

4、獲取成員變量構造函數信息            
 1 /**
 2  * 成員變量也是對象,是java.lang.reflect.Field這個類的的對象  3 * Field類封裝了關於成員變量的操做  4 * getFields()方法獲取的是全部public的成員變量的信息  5 * getDeclareFields()方法獲取的是該類本身聲明的成員變量的信息  6 */
 7 Field[] fs = c.getDeclaredFields();  8 for(Field field : fs){  9 //獲得成員變量的類型的類類型
10 Class fieldType = field.getType(); 11 String typeName = fieldType.getName(); 12 //獲得成員變量的名稱
13 String fieldName = field.getName(); 14 System.out.print(typeName+" "+fieldName); 15 } 16 
17 
18 /**
19  * 構造函數也是對象 20 * java.lang.Constructor中封裝了構造函數的信息 21 * getConstructor()方法獲取全部的public的構造函數 22 * getDeclaredConstructors獲得全部的構造函數 23 */
24 Constructor[] cs = c.getDeclaredConstructors(); 25 for(Constructor constructor : cs){ 26 System.out.print(constructor.getName()+"("); 27 //獲取構造函數的參數列表---》獲得的是參數雷彪的類類型
28 Class[] paramTypes = constructor.getParameterTypes(); 29 for(Class class1 : paramTypes){ 30 System.out.print(class1.getName()+","); 31 } 32 System.out.println(")"); 33 }

 


5、方法反射的基本操做函數

         一、如何獲取某個方法
            方法的名稱和方法的參數列表才能惟一決定某個方法
           Method m = c.getDeclaredMethod("方法名",可變參數列表(參數類型.class))
         二、方法的反射操做
             m.invoke(對象,參數列表)
             方法若是沒有返回值,返回null,若是有返回值返回Object類型,而後再強制類型轉換爲原函數的返回值類型
 
6、經過反射了解集合泛型的本質         
        
1 ArrayList list1 =newArrayList(); 2 ArrayList<String> list2 =newArrayList<String>(); 3 
4 Class c1 = list1.getClass(); 5 Class c2 = list2.getClass(); 6 
7 System.out.println(c1==c2);//結果爲true,爲何??

 

        結果分析:由於反射的操做都是編譯以後的操做,也就是運行時的操做,c1==c2返回true,說明編譯以後集合的泛型是去泛型化的。
                 那麼咱們就能夠理解爲,Java集合中的泛型,是用於防止錯誤類型元素輸入的,好比在list2中咱們add一個int,add(10)就會編譯報錯,那麼這個泛型就能夠只在編譯階段有效,經過了編譯階段,泛型就不存在了。能夠驗證,咱們繞過編譯,用反射動態的在list2中add一個int是能夠成功的,只是這時由於list2中存儲了多個不一樣類型的數據(String型,和int型),就不能用for-each來遍歷了,會拋出類型轉換錯誤異常ClassCastException



=======================華麗的分隔線======================================
補充資料:
 
7、關於Java類加載器內容的詳解
     一、類的加載
            當程序要使用某個類時,若是該類還未被加載到內存中,則系統會經過加載,鏈接,初始化三步來實現對這個類進行初始化
          ·加載:
               就是指將class文件讀入內存,併爲之建立一個Class對象,任何類被使用時系統都會創建一個Class對象
          ·鏈接:
               驗證:確保被加載類的正確性
               準備:負責爲類的靜態成員分配內存,並設置默認初始化值
               解析:將類中的符號引用替換爲直接引用
          ·初始化:
               局部變量保存在棧區:必須手動初始化
               new 的對象保存在堆區:虛擬機會進行默認初始化,基本數據類型初始化值爲0,引用類型初始化值爲null
 
       二、類加載的時機(只加載一次)
             如下時機僅表示第一次的時候
             ① 建立類的實例的時候
             ② 訪問類的靜態變量的時候
             ③ 調用類的靜態方法的時候
             ④ 使用反射方式來強制建立某個類或接口對應的java.lang.Class對象
             ⑤ 初始化某個類的子類的時候
             ⑥ 直接使用java.exe命令來運行某個主類
 
       三、類加載器
             負責將.class文件加載到內存中,併爲之生成對應的Class對象
             雖然咱們在開發過程當中不須要關心類加載機制,可是瞭解這個機制咱們就能更好的理解程序的運行
           
       四、類加載器的組成:
            ①Bootstrap ClassLoader 根類加載器
                  也被稱爲引導類加載器,負責Java核心類的加載,好比System類,在JDK中JRE的lib目錄下rt.jar文件中的類
            ②Extension ClassLoader 擴展類加載器
                  負責JRE的擴展目錄中jar包的加載,在JDK中JRE的lib目錄下ext目錄
            ③System ClassLoader 系統類加載器
                  負責在JVM啓動時加載來自java命令的class文件,以及classpath環境變量所指定的jar包和類路徑,主要是咱們開發者本身寫的類
 
        更多內容請參考《深刻理解JVM虛擬機》
 

 

相關文章
相關標籤/搜索