JAVA反射

一. 什麼是JAVA反射

java反射機制是在程序運行狀態中,對於任何一個類都可以知道這個類的屬性和方法。對於任何一個對象都可以調用它的任意一個方法。Java反射機制運行程序判斷分析任何一個類的結構,包括成員方法和變量,並調用任意一個對象的方法。java

二. JAVA反射的實例

在JDK中,主要由如下類來實現Java反射機制,這些類(除了第一個)都位於java.lang.reflect包中編程

Class類:表明一個類,位於java.lang包下。設計模式

Field類:表明類的成員變量(成員變量也稱爲類的屬性)。api

Method類:表明類的方法。數組

Constructor類:表明類的構造方法。app

 

Class類ide

Class類是用來保存運行時類型信息的類。每一個類(型)都有一個Class對象。運行程序時,Java虛擬機(JVM)首先檢查是否所要加載的類對應的Class對象是否已經加載。若是沒有加載,JVM就會根據類名查找.class文件,並將其Class對象載入。基本的 Java 類型(boolean、byte、char、short、int、long、float 和 double)和關鍵字 void 也都對應一個 Class 對象。通常當某個類的Class對象被載入內存時,它就能夠用來建立這個類的全部對象。this

經常使用的獲取Class對象的3種方式:url

1.使用Class類的靜態方法。例如:  spa

Class.forName("java.lang.String");

2.使用類的.class語法。如:

String.class;

3.使用對象的getClass()方法。如:

String str = "aa";
Class<?> classType1 = str.getClass();

 

Field類

再來看看經過class對象來獲取類的屬性

private  String strName;
     private  int  sum;
     public  String strTest;
      
     public  static  void  main(String[] args)  throws  NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
          
         ReflectTest rTest= new  ReflectTest();
         Class<?> clsType = ReflectTest. class ;
         //得到當前類和父類中的public類型的全部屬性
         Field[] fields=clsType.getFields();
         for (Field field:fields)
             System.out.println(field);
     }

經過反射來設置屬性值

public  class  ReflectTest {
      
     private  String strName;
     private  int  sum;
     public  String strTest;
      
     public  void  show(){
         System.out.println(strName);
         System.out.println( "" +sum);
         System.out.println(strTest);
     }
      
     public  static  void  main(String[] args)  throws  NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
          
         ReflectTest rTest= new  ReflectTest();
         Class<?> clsType = ReflectTest. class ;
         //獲取三個屬性值
         Field field1=clsType.getDeclaredField( "strName" );
         Field field2=clsType.getDeclaredField( "sum" );
         Field field3=clsType.getField( "strTest" );
         //反射設置對象的屬性值
         field1.set(rTest,  "james" );
         field2.set(rTest,  10 );
         field3.set(rTest,  "reflect field" );
          
         rTest.show();
     }
}

 

Method類

在獲取Class對象後,咱們還能夠獲取類所對應的方法,這時就要用到咱們的Method類

public  class  ReflectTest {
  
     private  void  fun() {
         System.out.println( "this is fun()" );
     }
  
     private  void  add( int  a,  int  b) {
         System.out.println( "the sum is: "  + (a + b) +  " " );
     }
  
     public  static  void  main(String[] args) {
         Class<?> clsType = ReflectTest. class ;
         // 返回class對象所對應的類或接口中,所聲明的全部方法的數組(包括私有方法)
         Method[] methods = clsType.getDeclaredMethods();
         // 打印出全部的方法名
         for  (Method method : methods) {
             System.out.println(method);
         }
     }
}

再來看看怎麼反射調用對象的方法

public  class  ReflectTest {
  
     private  void  fun() {
         System.out.println( "this is fun()" );
     }
  
     public  int  add( int  a,  int  b) {
         System.out.println( "the sum is: "  + (a + b) +  " " );
         return  a+b;
     }
  
     public  static  void  main(String[] args)  throws  NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
         ReflectTest rTest= new  ReflectTest();
         Class<?> clsType = ReflectTest. class ;
         // 返回對應public類型的方法,第一個參數爲方法名,第二個參數爲方法的參數列表
         Method method=clsType.getMethod( "add" new  Class<?>[]{ int . class , int . class });
         System.out.println( "" +method.invoke(rTest,  new  Object[]{ 5 , 6 }));
     }
}

 

Constructor類

在獲取Class對象後,咱們就能夠經過它建立類的對象啦。這就地用到咱們的Constructor類啦。有三種建立對象的方式:

1.先得到Class對象,而後經過該Class對象的newInstance()方法直接生成便可:

Class<?> classType = StringBuffer.class;

Object obj = classType.newInstance();

2.固然也能夠再得到Class對象後,經過該Class對象得到類的Constructor對象,再經過該Constructor對象的newInstance()方法建立對象

Class<?> clsType=StringBuffer.class;

// 得到Constructor對象,此處獲取一個無參數的構造方法的
Constructor<?> constructor=clsType.getConstructor(new Class<?>[]{});

// 經過Constructor對象的構造方法來生成一個對象
Object obj=constructor.newInstance(new Object[]{});

三、若是構造器帶有參數,那就不能有上面兩個方法來建立對象了,可以使用下面這一種方式:

Class<?> clsType = StringBuffer.class;

Constructor<?> constructor = clsType.getConstructor(new Class<?>[] { String.class });

Object obj = constructor.newInstance(new Object[] { "hello, classtest" });

三. JAVA反射的原理

 Class.forName(classname)的執行過程:

其實是調用了Class類中的 Class.forName(classname, true, currentLoader)方法。參數:name - 所需類的徹底限定名;initialize - 是否必須初始化類;loader - 用於加載類的類加載器。currentLoader則是經過調用ClassLoader.getCallerClassLoader()獲取當前類加載器的。類要想使用,必須用類加載器加載,因此須要加載器。反射機制,不是每次都去從新反射,而是提供了cache,每次都會須要類加載器去本身的cache中查找,若是能夠查到,則直接返回該類。

java的類加載器,它分爲BootStrap Class Loader(引導類加載器),Extensions Class Loader (擴展類加載器),App ClassLoader(或System Class Loader),固然少不了Custom ClassLoader(用戶自定義類加載器)。其加載過程當中會先檢查類是否被已加載,檢查順序是自底向上,從Custom ClassLoader到BootStrap ClassLoader逐層檢查,只要某個classloader已加載就視爲已加載此類,保證此類只全部ClassLoader加載一次。而加載的順序是自頂向下,也就是由上層來逐層嘗試加載此類。

流程圖:

類加載器的類加載過程,先檢查本身是否已經加載過該類,若是加載過,則直接返回該類,若沒有則調用父類的loadClass方法,若是父類中沒有,則執行findClass方法去嘗試加載此類,也就是咱們一般所理解的片面的"反射"了。這個過程主要經過ClassLoader.defineClass方法來完成。defineClass 方法將一個字節數組轉換爲 Class 類的實例(任何類的對象都是Class類的對象)。這種新定義的類的實例須要使用 Class.newInstance 來建立,而不能使用new來實例化。

在運行期間,若是咱們要產生某個類的對象,Java虛擬機(JVM)會檢查該類型的Class對象是否已被加載。若是沒有被加載,JVM會根據類的名稱找到.class文件並加載它。一旦某個類型的Class對象已被加載到內存,就能夠用它來產生該類型的全部對象。

四. JAVA反射的應用-動態代理

說動態代理以前,必須先講講代理的設計模式。代理模式爲其餘對象提供一種代理以控制對這個對象的訪問。在某些狀況下,一個客戶不想或者不能直接引用另外一個對象,而代理對象能夠在客戶端和目標對象之間起到中介的做用。代理模式的有三個角色定義:

1)抽象主題接口(Subject)
聲明真實對象和代理對象的共同接口。代理模式中,代理類和真實對象都實現了接口。
2)真實主題類(RealSubject)
真正實現業務邏輯的類。
3)代理類(Proxy)
用來代理和封裝真實主題。代理對象角色內部含有對真實對象的引用,從而能夠操做真實對象。同時,代理對象能夠在執行真實對象操做時,附加其餘的操做,至關於對真實對象進行封裝。一般狀況下,代理模式中的每個代理類在編譯以後都會生成一個class文件,代理類所實現的接口和所代理的方法都被固定,這種代理被稱之爲靜態代理(Static Proxy),
缺點:1. 若是須要爲不一樣的真實主題類提供代理類,都須要增長新的代理類,這將致使系統中的類個數急劇增長。
           2. 靜態代理類和真實主題類實現了相同的接口,代理類經過真實主題類實現了相同的方法,若是接口增長一個方法,除了全部實現類須要實現這個方法外,全部代理類也須要實現此方法,增長了代碼維護的複雜度。

這就須要使用咱們的動態代理了。

動態代理(Dynamic Proxy)在系統運行時動態建立代理類,可讓系統可以根據實際須要來動態建立代理類,讓同一個代理類可以代理多個不一樣的真實主題類。動態代理是一種較爲高級的代理模式,它在事務管理、AOP(Aspect-OrientedProgramming,面向方面編程)等領域都發揮了重要的做用。Java語言提供了對動態代理的支持,在Java的動態代理機制中,有兩個重要的類或接口,一個是 InvocationHandler接口、另外一個則是 Proxy類。Proxy這個類的做用就是用來動態建立一個代理對象,一般使用newProxyInstance 方法建立代理對象。Proxy代理類動態建立代理對象都須要關聯到一個InvocationHandler接口,當咱們經過代理對象調用一個方法的時候,這個方法的調用就會被轉發爲由InvocationHandler這個接口的 invoke方法來進行調用。

動態代理的步驟
(1).建立一個實現接口InvocationHandler的類,它必須實現invoke方法
(2).建立被代理的類以及接口
(3).經過Proxy的靜態方法
newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) 建立一個代理
(4).經過代理調用方法

類圖

Dynamic Proxy
1 )抽象接口
public  interface  Subject
{
     public  void  doSomething(String str);
}
2 )真實對象
public  class  RealSubject  implements  Subject
{
  
     @Override
     public  void  doSomething(String str)
     {
         // TODO Auto-generated method stub
         System.out.println( "do something: "  + str);
     }
}
      
3 )動態代理Handler類
public  class  MyInvocationHandler  implements  InvocationHandler
{
     //被代理的對象
     private  Object target= null ;
   
     public  MyInvocationHandler(Object obj)
     {
         this .target=obj;
     }
   
     @Override
     public  Object invoke(Object proxy, Method method, Object[] args)  throws  Throwable
     {
         // TODO Auto-generated method stub
         return  method.invoke(target, args);
     }
}
  
  
4 )場景類Client
public  class  Client
{
     public  static  void  main(String[] args)
     {
         //real object
         Subject subject= new  RealSubject();
         //handler
         InvocationHandler handler= new  MyInvocationHandler(subject);
         //得到代理proxy
         Subject proxy=(Subject) Proxy.newProxyInstance(subject.getClass().getClassLoader(), subject.getClass().getInterfaces(), handler);
   
         System.out.println(proxy.getClass().getName());
         proxy.doSomething( "yes" );
  }
}
相關文章
相關標籤/搜索