Java 反射 (Class、ClassLoader、Constructor、Method、Field)

 

 

 反射是Java中一個很是重要、很是強大的機制。曾看到一句話「反射是框架的靈魂」,初學時不懂,等到學完框架以後才慢慢理解其意。html

什麼是反射?咱們先經過幾個類和示例來初步體會一下反射。 java

1、ClassLoader類bootstrap

什麼是類加載器?  
數組

ClassLoader是一個抽象類,它的實例是類加載器。磁盤上存在的xxx.class文件須要被加載進JVM才能執行。類加載器則是負責加載.class文件的對象,而後在JVM中生成該類的Class對象。每個Class對象都關聯着定義它的那個類加載器。數組的類加載器與其元素的加載器是同一個,若是元素類型是基本類型,則數組沒有類加載器。框架

類加載器工做原理ide

類加載器都有一個與之關聯的父加載器,當加載器須要加載一個文件時,它首先將該任務」委派」給父加載器,若是父加載器沒法加載該文件,再本身進行加載。JVM的引導加載器(bootstrap class loader)沒有父加載器,但可做爲父加載器。關於類加載器更詳細的分析 點擊這裏post

2、Class類測試

什麼是Class<T>類?this

Class不是咱們日常聲明類時所用的關鍵字class,它是一個類,它的對象用來描述一個運行狀態的類或接口。一個xxx.java文件編譯後生成一個xxx.class文件,一個xxx.class文件被JVM加載後生成該類對應的Class對象,該對象包含了該類的全部信息,好比,類中有字段、構造器、方法等信息。一個類有一個對應的Class對象,元素類型相同且長度相同的數組共享一個Class對象,java基本類型包括void也都有各自的Class對象。Class是一個泛型類,若是不加泛型,須要強轉。spa

如何獲取一個類的Class對象?

Class沒有public構造器,當類被加載時,JVM會經過調用ClassLoader的defineClass方法來自動建立該類的Class對象。

獲取一個類的Class對象有三種方式:

1)類名.class

2)  該類的對象.getClass()

3)  Class.forName(String 類名) (包名加類名)  

Class對象有何做用?

下面列出幾個Class類的方法:

1)   獲取類加載器:

 getClassLoader()

2)  獲取Constructor構造器對象:

 getConstructor(Class... parameterType)                               獲取具備指定參數的公共構造器對象

 getConstructors()                                                                   獲取全部公共構造器對象

 getDeclaredConstructor(Class... parameterType)         獲取具備指定參數的構造器對象

 getDdclaredConstructors()                                                     獲取全部構造器對象

3)  獲取Method對象:

  getMethod(String name,Class...parameterType)        獲取具備指定名稱和參數的公共方法對象

  getMethods()                                                                               獲取全部公共方法對象

  getDeclaredMethod(String name,Class...parameterType)          獲取具備指定名稱和參數的方法對象

  getDeclaredMethods()                  獲取全部的方法對象

4)獲取Field字段對象:

  getField(String name)                      獲取具備指定名稱的公共字段對象

  getFields()                        獲取具備全部公共字段對象

  getDeclaredField(String name)               獲取具備指定名稱的字段對象

  getDeclaredFields()                      獲取全部字段對象

 5)獲取Class對象所表明的類的一個對象(很是重要的一個方法)

  Object  newInstance()                                                                  用默認的無參數構造器建立一個對象

(帶Declared的get方法能夠獲取任意訪問權限的成員,不帶Declared的只能獲取public成員)

測試Class,更多的測試在其餘類的測試中體現。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package  cn.edu;
 
public  class  User {
 
     private  String username;
     
     private  int  age;
     
     public  User() {
         
     }
     
     public  User(String username ,  int  age) {
         this .username = username;
         this .age = age;
     }
     private  User(String username) {
         this .username = username;
     }<br>
      public  void  say(String str) {<br>        System.out.println(str);<br>     }<br><br>
     @Override
     public  String toString() {
         return  "User [username="  + username +  ", age="  + age +  "]" ;
     }
     
     
}

  

1
2
3
4
5
6
7
8
9
10
11
12
@Test
     public  void  fun2()  throws  Exception {
         Class userClass = User. class ;
         
         System.out.println(userClass.getName());                    //獲取該class對象的類的類名
         
         
         System.out.println(userClass.getClassLoader().toString());  //獲取類加載器
         
         User user = (User)userClass.newInstance();                  //調用默認的無參數構造器建立對象
         System.out.println(user.toString());
     }

  運行結果:

 

3、Constructor類

構造器類,封裝構造器的有關信息。

主要方法 Object newInstance(Object...arg)                                  用指定參數建立對象

測試Constructor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Test
     public  void  fun2()  throws  Exception {
         Class userClass = User. class ;
 
         Constructor userConstructor1 = userClass.getConstructor(String. class  int . class );  //有參構造器
         User user1 = (User)userConstructor1.newInstance( "小紅" , 18 );
         System.out.println(user1.toString());
         
         Constructor userConstructor2 = userClass.getConstructor();                           //無參構造器
         User user2 = (User)userConstructor2.newInstance();
         System.out.println(user2);
         
         Constructor userConstructor3 = userClass.getDeclaredConstructor(String. class );       //私有構造器
         userConstructor3.setAccessible( true );                                                //開啓訪問權限
         User user3 = (User)userConstructor3.newInstance( "小明" );
         System.out.println(user3);
     }

  運行結果:

默認是不能夠訪問private成員的,userConstructor3.setAccessible(true)是用於開啓訪問權限的,這樣就能夠訪問了,感受是開掛同樣!

 

4、Method類 

方法類,封裝方法的有關信息

主要方法

Object invoke(Object obj , Object... args)                                     調用obj對象的Method對象表明的方法,args爲參數

測試Method:

 

1
2
3
4
5
6
7
8
@Test
     public  void  fun2()  throws  Exception {
         Class userClass = User. class ;                                
         User user = (User)userClass.newInstance();                            //用默認無參數構造方法建立對象
         
         Method method = userClass.getMethod( "say" ,String. class );              //獲取名爲"say",參數爲string的method對象
         method.invoke(user, "hello" );                                          //調用user的say方法
     }

  運行結果:

 

5、Field類

字段類,封裝字段的有關信息

主要方法

Object get(Object obj)                                                                   獲取obj對象的此Field對象表明的字段的值

void set(Object obj , Object value)              設置obj對象的此Field對象表明的字段的值

 測試Fidle

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Test
     public  void  fun2()  throws  Exception {
         Class userClass = User. class ;
         Constructor userConstructor = userClass.getConstructor(String. class , int . class );
         User user = (User)userConstructor.newInstance( "小明" , 18 );                                 
         
         System.out.println(user.toString());
         
         Field userField = userClass.getDeclaredField( "username" );   //獲取username字段
         userField.setAccessible( true );                              //開啓訪問權限
         userField.set(user,  "小紅" );                                 //給user對象的該字段設置值
         
         System.out.println(user.toString());
     }

   運行結果:

 

 

  到此關於反射的幾個類就簡單的認識了一下,咱們能夠不用new關鍵字來建立對象,調用對象的方法也與傳統的調用方式有很大區別,咱們甚至能夠操做private成員(雖然這樣作破壞了封裝性),相對於傳統的操做方式,反射更像是一種逆向思惟,之前操做成員,主體在於對象,而反射的主體在於Class和成員自己。到此咱們對反射有了初步的認識。接下在敘述一個重要的概念,有助於咱們更好的理解反射。

6、動態加載與靜態加載

注意此處所說的加載是針對編譯的,將xxx.java轉化成xxx.class,而不是運行的加載字節碼。Java建立對象的經常使用方式是使用new 關鍵字,如 User user  = new User(); 這種是靜態加載,即在編譯期就已經獲取User類的有關信息,若是User類不存在或有錯誤,編譯會報錯。動態加載就是用上面的Class.forName("包名.User");來加載User類,若是User類不存在或是有錯誤,在編譯時是不會報錯的,由於根本沒去加載User類。只有當程序運行到該處,JVM纔會去加載User,而動態加載則是依賴反射實現的。

 

好了,能夠給出最終概念了,經過上面的重重示例與解析,應該不難理解反射了。

7、反射  

  Java反射機制是指java程序在運行過程當中,能夠獲取任意一個類的相關信息,而且可以調用其方法和屬性,這種動態獲取信息和動態調用方法的功能叫作Java的反射機制。

      上面寫了這麼多,總結起來也就這一句話。

 

 本文我的編寫,水平有限,若有錯誤,懇請指出,歡迎討論分享。

 

 
分類:  Java
 
 
« 上一篇:  八大排序之冒泡排序 
» 下一篇:  類加載器 ClassLoder詳解
相關文章
相關標籤/搜索