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