Java的反射機制可以動態(tài)的加載類,實例化對象,動態(tài)的調(diào)用對象的方法等等。可以說Java的反射機制異常的強大。而且在很多的高級框架中都得到了應用。也可能說,Java的反射是高級框架功能實現(xiàn)的重要的一部分,所以,學好Java的反射機制對于我們高級框架的深入學習尤為重要。
Java中有的java.lang.Class對象代表Java應用程序運行時所加載的類或者接口的實例。Java中的每一個類都有一個java.lang.Class對象向?qū)?。要獲得java.lang.Class的對象有兩種辦法,直接通過類的.class來獲得,或通過類實例例化出來的對象的getClass()方法獲得。
-
- Class clazz=String.class;
-
- String str="work";
- Class clazz1=str.getClass();
//第一種Class clazz=String.class;//第二種String str="work";Class clazz1=str.getClass();
這樣,我們就獲得了String類的Class實例。有了Class實例,我們就可以對其相對應的類進行實例化對象,方法的調(diào)用,域的修改等操作。
我們還可以通過
Class.forName()方法來加載類,獲得Class的實例。在寫數(shù)據(jù)庫的時候,我會都接觸到,用它來加載數(shù)據(jù)庫驅(qū)動類。forName()還有個重載的方法,除了傳遞一個類,還可以傳遞一個布爾型的值,如果為false,那么將不會初始化加載的類(一般用到它是因為類中的靜態(tài)域的問題)。
我們還可以通過類的加載器,ClassLoader實例的
loadClass()方法來加載類。獲得ClassLoader的方法:
ClassLoader loader=類名.class.getClassLoader(); 下面我們對類的加載器進行下簡要的分析:
每個類加載器再加載類之前,會先讓父類加載器先加載,如果父類加載器找不到要加載的類,再交給自己來加載。Java中有3個類加載器,
BootstrapLoader,ExtClassLoader,AppClassLoader(SystemLoader)。他們會按照
BootstrapLoader -> ExtClassLoader -> AppClassLoader這個順序去加載類,如果找不到類,則會丟出NotClassDefFoundError異常。用ClassLoader來加載類是,不會初始化類的靜態(tài)區(qū)域,只有再真正使用到這個類的時候,才初始化靜態(tài)區(qū)域。前面說過,每個類有一個Class的實例對象,但是,如果同一個類,被兩個加載器都加載過,就會有兩個Class的實例與它對應。
用反射生成對象,我們可以調(diào)用Class中的newInstance()方法來生成對象,例如
- Class clazz=String.class;
-
- Object obj=clazz.newInstance();
Class clazz=String.class;//生成一個Object類型的對象Object obj=clazz.newInstance();
生成的對象都是Object類型的。但是有一點要注意,用這種方法生成對象,類中必須有一個無參的構(gòu)造器。另外,還有一種生成對象的辦法,用java.lang.reflect.Constructor這個類來生成對象。用這種方法來生成對象可以不必有無參的構(gòu)造器,我們以String類為例,用Constructor來實例化一個String類的對象,用其public String(String arg0)構(gòu)造器為例子:
-
- Class clazz=String.class;
-
- Class[] param=new Class[1];
-
- param[0]=String.class;
-
- Constructor constructor=clazz.getConstructor(param);
-
- Object[] obj=new Object[1];
-
- obj[0]="zhang3";
-
- String str=(String)constructor.newInstance(obj)
//獲得String的Class實例Class clazz=String.class;//創(chuàng)建一個數(shù)組,這個數(shù)組用來放要實例化對象的構(gòu)造器里的參數(shù)類型Class[] param=new Class[1];//放入構(gòu)造器中的參數(shù)類型,如果有多個,按順序放入param[0]=String.class;//實例化一個構(gòu)造器對象,并把放著構(gòu)造器參數(shù)類型的數(shù)組作為參數(shù)傳進去Constructor constructor=clazz.getConstructor(param);//創(chuàng)建一個Object數(shù)組,用于放構(gòu)造器中對應的值Object[] obj=new Object[1];//將值放入到數(shù)組中,這里要注意,param數(shù)組中放入的是什么類型,這里就要按順序放入obj[0]="zhang3";//實例化對象,并把放著構(gòu)造器要傳入的參數(shù)的數(shù)組傳到方法中String str=(String)constructor.newInstance(obj)
這樣,我們就通過java.lang.reflect.Constructor實例化出來了String類型的對象。
用反射調(diào)用方法,通過java.lang.reflect.Method類,我們來實現(xiàn)對方法的調(diào)用,代碼如下:
-
- Method method=clazz.getMethod(arg0, arg1);
-
- method.invoke(arg0, arg1);
//實例化一個方法的對象,arg0是方法名,arg1是方法的參數(shù)類型,要是多個就傳數(shù)組Method method=clazz.getMethod(arg0, arg1);//方法的調(diào)用,arg0是有次方法的對象,arg1是傳入方法中的值method.invoke(arg0, arg1);
上面的兩行代碼是最主要的,下面我貼出一個完整例子的代碼,對照的看下,馬上就明白,假設(shè)有一個Student類,它有一個setName方法,方法參數(shù)類型為String:
- Class clazz=Student.class;
- Object obj=clazz.newInstance();
- Class[] param={String.class};
- Method method=clazz.getMethod("setName", param);
- Object[] value={"zhang3"};
- method.invoke(obj, value);
Class clazz=Student.class;Object obj=clazz.newInstance();Class[] param={String.class};Method method=clazz.getMethod("setName", param);Object[] value={"zhang3"};method.invoke(obj, value);
以上便是反射對方法的調(diào)用,基本上與實例化對象大同小異。
用反射修改域,通過java.lang.reflect.Field來實現(xiàn),下面我直接貼出完整例子的代碼,還是以Student類為例:
- Class clazz=Student.class;
- Object obj=clazz.newInstance();
-
- Field name=clazz.getField("name");
-
- name.setAccessible("true");
-
- name.set(obj, "zhang3");
Class clazz=Student.class;Object obj=clazz.newInstance();//獲得一個域的實例,getField()方法中的參數(shù)為域的名字Field name=clazz.getField("name");//*如果你的域修飾為private,那么必須調(diào)用setAccessible("true")才能對其修改name.setAccessible("true");//第一個參數(shù)是有此域的對象,第二個是值,這個方法是通用的,不管你的域類型是什么name.set(obj, "zhang3");
Java的反射機制非常的強大,在這里我只是對其一些基本的功能如加載類,實例化對象,對類中方法的調(diào)用等功能進行了簡單的講解和分析。再后面,我還會對反射中的代理進行簡要概述