PS:最近數據庫算是學完了,總算是有時間回頭復習一下以前學的東西了...最近這幾天好好復習了一下反射機制,順便也做個總結...JAVA的學習基本都沒有去寫...慢慢補上吧...
學習內容:
1.反射機制的概念...
2.反射機制的作用...
3.如何去使用反射機制...
4.反射機制的優勢...
1.反射機制:
反射機制,想必大家都不陌生,但是估計大家也就是知道個概念...那么到底什么是反射機制,估計大家對這個概念有點模糊不清...簡單的概念就是:對於我們定義的每一個類,在任何的時刻,我們都能夠知道這個類里面的屬性和方法...對於任何一個對象,都能夠調用這個類中的方法...這就是反射機制的基本概念..
2.反射機制實現的功能:
在運行時判斷任意一個對象所屬的類,在運行時構造任意一個類的對象,在運行時判斷任意類所具有的方法和屬性,在運行時調用任意一個對象的方法
生成動態代理...
3.如何使用反射機制:
我們如何去使用反射機制呢?
反射機制里一個特點就是實例化class對象,因為任意一個類對象都是class的實例...那么如何實例化class對象呢?三種方法:
i.通過forname()方法...
ii.對象.getclass();
iii.類.class;
在程序運行時通過實例化的對象,然后對象使用反射來調用類內的方法或者是屬性...這樣就實現了動態的獲取信息...
package Fanshe; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Scanner; class person{ private String name; private String sex; private int age; person(){ } public person(String name,String sex,int age){ super(); this.name=name; this.sex=sex; this.age=age; } private void setname(String name){ this.name=name; } public String getsex(){ return sex; } public void setsex(String sex){ this.sex=sex; } public int getage(){ return age; } public void setage(){ this.age=age; } public String toString(){ return "姓名 :"+name+"年齡: "+age; } } class reflectdemo{ reflectdemo(){ Scanner cin=new Scanner(System.in); String classpath=cin.nextLine();//需要輸入類的完整路徑...我的路徑是Fanshe.person try { //三種方式實現對象的實例化 Class cla=Class.forName(classpath); // Class cla_1=person.class; // person p=new person(); // Class cla_2=p.getClass(); Method [] method=cla.getDeclaredMethods();//定義一個數組來保存類中所有的方法... System.out.println("=====類中的方法有====="); for(Method meth:method){ System.out.println(meth.toString()); } System.out.println("=====方法獲取結束====="); Field [] field=cla.getDeclaredFields(); //保存類中所有屬性 System.out.println("=====類中內部的屬性====="); for(Field fie:field){ System.out.println(fie.toString()); } System.out.println("=====屬性獲取結束====="); Constructor [] con=cla.getDeclaredConstructors();//類中所有的構造函數... System.out.println("=====獲取構造函數====="); for(Constructor c:con){ System.out.println(c.toString()); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); System.out.println("路徑輸入錯誤"); } } } public class fanshe_2 { public static void main(String[] args) { // TODO Auto-generated method stub reflectdemo ref=new reflectdemo(); } }
上述代碼通過使用反射機制來獲取類中的方法和屬性...反射這東西我還是用代碼說話更好一些,所以我還是都使用代碼來讓大家理解的更深刻一些...
package Fanshe_cll; import java.lang.reflect.Method; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.annotation.Annotation; class reflectdemo{ //私有構造函數 private reflectdemo(String name,int age){ } reflectdemo(){ } //公有構造函數 public reflectdemo(String name){ } public void info(){ } public void info(String str){ } private void set(){ } class inner{ }//內部類... } import Fanshe_cll.reflectdemo.inner; public class clear1 { public static void main(String[] args) throws Exception { // TODO Auto-generated method stub reflectdemo r=new reflectdemo(); Class claz_1=r.getClass(); System.out.println(claz_1.toString()); Class<reflectdemo> cla=reflectdemo.class; //使用泛型,用class來實例化對象.. System.out.println(cla.toString()); Class claz=Class.forName("Fanshe_cll.reflectdemo"); System.out.println(claz.toString()); System.out.println("=====類中的構造函數====="); //獲取全部的構造函數.... Constructor []constructor=cla.getDeclaredConstructors();//返回類中所有的構造函數,無論是公有的還是私有的...並且這個保存是按照倒序保存的..
就是constructor[0]保存的是最后一個構造函數...而constructor[length-1]保存的是第一個構造函數...比如說我們有一個person類,類中有四個構造方法...
public Person(){}
public Person(String name){this.name=name;}
public Person(int age){this.age=age;}
public Person(String name,int age){this.age=age;this.name=name;}
我們在主函數來調用這幾種方法...
Class<?> cla=Person.class;
Constructor []con=cla.getDeclaredConstructors();
Person per_1=(Person) con[0].newInstance("劍神",20);
Person per_2=(Person) con[1].newInstance(20);
Person per_3=(Person) con[2].newInstance("劍聖");
Person per_4=(Person) con[3].newInstance(); 必須這樣傳參才行,如果把他們倒過來,那么必定會出現java.lang.illegalArgumentException非法參數異常..
for(Constructor con:constructor){ System.out.println(con.toString()); } System.out.println("公有構造函數"); //獲取公有的構造函數.... Constructor []constructor_1=cla.getConstructors();//這個就和上面不同了,這個保存着類中所有的公有構造函數 for(Constructor con_1:constructor_1){ System.out.println(con_1.toString()); } System.out.println("獲取全部方法"); //獲取全部的方法.... Method [] method =cla.getDeclaredMethods(); for(Method meth:method){ System.out.println(meth.toString()); } //獲取公有的方法.... System.out.println("獲取公有方法"); Method [] method_1=cla.getMethods(); for(Method meth_1:method_1){ System.out.println(meth_1.toString());//這個輸出異常處理的函數..這說明有些方法是帶有異常的... } //獲取指定的某個方法.... System.out.println("獲取某個指定的方法"); Method method_2=cla.getMethod("info",null);//獲取參數為空的info方法.. System.out.println(method_2.toString()); Method method_3=cla.getMethod("info", String.class);//獲取指定參數為字符串的方法... System.out.println(method_3.toString()); // Method method_4=cla.getMethod("info", reflectdemo.class); System.out.println("-----------------"); // System.out.println(method_4.toString()); System.out.println("-----------------"); //獲取注釋 System.out.println("獲取注釋"); Annotation []annotations =cla.getAnnotations(); for(Annotation ant:annotations){ System.out.println(ant.toString()); } //獲取包信息 System.out.println("獲取包信息"); Package package_1=cla.getPackage(); System.out.println(package_1.toString()); //獲取內部類 System.out.println("獲取內部類"); Class [] cla_1=cla.getDeclaredClasses(); for(Class clazz : cla_1){ System.out.println(clazz.toString()); } //調用父類 System.out.println("調用父類"); Class cla_2=cla.getSuperclass(); System.out.println(cla_2.toString()); //內部類對象... Class cla_3=Class.forName("Fanshe_cll.reflectdemo$inner"); // Class cla_4=inner.class; //獲取內部類的外部類 System.out.println("使用內部類來獲取外部類信息"); System.out.println(cla_3.getDeclaringClass()); System.out.println("--------------"); System.out.println(cla_3.getPackage()); System.out.println(cla_3.getSuperclass()); } }
我們上面只是返回類中的方法...基本沒有進行調用,那么下面就對上面的那些方法進行調用...
package Fanshe_c; import java.lang.reflect.Method; class person{ //這個類大家基本不用看,是一個很普通的類,之所以粘出來是因為自己定義了好幾個person類,怕產生混淆.... private String name; private String sex; private int age; person(){ } public person(String name,String sex,int age){ super(); this.name=name; this.sex=sex; this.age=age; } public void setname(String name){ this.name=name; } public String getname(){ return name; } public String getsex(){ return sex; } public void setsex(String sex){ this.sex=sex; } public int getage(){ return age; } public void setage(int age){ this.age=age; } public String toString(){ return "姓名 :"+name+"年齡: "+age; } } public class cll_2 { public static void main(String[] args) throws Exception{ // TODO Auto-generated method stub Class cla=person.class; person p=new person();//定義一個對象..使用new關鍵字.. Method method=cla.getMethod("setname",String.class); method.invoke(p, "劍神");//這句話還可以寫成 method.invoke(cla.newInstance(),"劍神");下面凡是和這句話類似的都可以使用這種方法,使用newInstance()的默認構造函數去實例化一個對象.. Method method_1=cla.getMethod("setage",int.class); method_1.invoke(p, 20); Method method_2=cla.getMethod("setsex", String.class); method_2.invoke(p, "男"); //調用輸出方法... Method method_3=cla.getMethod("getname", null); System.out.println((method_3.invoke(p, null)).toString()); Method method_4=cla.getMethod("getsex", null); System.out.println((method_4.invoke(p,null)).toString()); Method method_5=cla.getMethod("getage", null); System.out.println((method_5.invoke(p, null)).toString()); } }
通過上面的代碼,大家難免會產生一個疑問,就是使用new和newInstance去創建對象到底有什么區別呢??這個我也在這里進行下解釋...
new與newInstance()的區別
首先一個是關鍵字,一個是方法...new是一個關鍵字,使用關鍵字來新建一個對象的時候沒有過多的限制...而newInstance()是一個方法,使用newInstance()創建對象的時候,類一定要有一個默認的無參構造方法...並且這個類必須要被加載,否則的話JVM首先會將這個類進行加載后,然后我們才能使用newInstance()來新建一個對象...那么大家就會問了,那給出兩個創建對象的方法,是不是有點多此一舉了呢?這個當然是否定的...使用newInstance()...其實在一定情況下是很有好處的...大家來看:
Class c = Class.forName("darker");
factory = (AInterface)c.newInstance();
其中AInterface是darker的接口,在看下面
String className = "darker";
Class c = Class.forName(className);
factory = (AInterface)c.newInstance();
進一步的進行擴展...
String className = readfromXMlConfig;//從xml 配置文件中獲得字符串
Class c = Class.forName(className);
factory = (AInterface)c.newInstance();
上面代碼把類名darker徹底的去掉了,這個優點估計大家都看出來了,這樣當我們的darker類做怎樣的修該,我們這句話永遠保持不變...如果使用new就沒辦法了吧??
這就是newInstance()在一定的地方使用的好處...
簡單的介紹一下setAccessible()函數...
package Fanshe_fuzhi; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.InvocationTargetException; import java.util.Calendar; class Person { //這個代碼目的是為了簡單的介紹一下setAccessible()函數。。 private String name; private String gender; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getSay(){ return"姓名:"+this.name+" \t性別:"+this.gender+"\t年齡:"+this.age; } } public class Fanshe_quanxian { public static void main(String[] args) throws Exception{ // TODO Auto-generated method stub Class<?> cla=Person.class; Field name=cla.getDeclaredField("name"); System.out.println(name); name.setAccessible(true); //這個函數的參數是boolean類型,當值為true的時候,那么JVM將忽略訪問時的權限...如果為false,那么就不進行忽略... name.set(cla.newInstance(),"三豐"); Field gender=cla.getDeclaredField("gender"); gender.setAccessible(true); gender.set(cla.newInstance(),"重陽"); Field age=cla.getDeclaredField("age"); age.setAccessible(true); age.setInt(cla.newInstance(), 20); Method getsay=cla.getMethod("getSay"); Object obj=getsay.invoke(cla.newInstance()); System.out.println(obj.toString()); } }
Modifyers函數...很簡單,沒什么東西...
package Fanshe_modifier; import Fanshe_Person.Person; import java.lang.reflect.Field; import java.lang.reflect.Modifier; public class Modify { public static void main(String[] args) { // TODO Auto-generated method stub Class<?> cla = Person.class; Field[] field = cla.getDeclaredFields(); for (int i = 0; i < field.length; i++) { int mo = field[i].getModifiers(); System.out.println(mo); String priv = Modifier.toString(mo); System.out.println(priv); Class<?> type =field[i].getType(); System.out.println(type.getName()); System.out.println(field[i].getName()); /* * 直接toString就可以返回方法的名字... * field[i].toString * */ } } }
動態創建和訪問數組...
package Array; import java.lang.reflect.Array; public class Array_1 { public static void main(String[] args) { // TODO Auto-generated method stub Object obj=Array.newInstance(int.class,3); /* *一維數組的創建與賦值.. */ Array.set(obj, 0, 20); Array.set(obj, 1, 30); int obj_1=Array.getLength(obj); System.out.println(obj_1); Object obj_2=Array.get(obj, 0); System.out.println(obj_2); Array.set(obj, 2, 40); int obj_3=Array.getLength(obj); System.out.println(obj_3); Object two_arr=Array.newInstance(int.class, 10,10); /* * 二維數組的賦值:先找到第一維的下標值..然后再傳參.. * */ Object firstindex=Array.get(two_arr, 0); Array.set(firstindex, 0, 20); } }
動態修改數組的大小...
package Array; import java.lang.reflect.Array; public class Array_xiugailength { public static void main(String[] args) { // TODO Auto-generated method stub int temp[]={0,1,2,3,4,5,6,7,8,9}; int newtemp[]=(int [])arrayinc(temp,15); for(int i=0;i<newtemp.length;i++){ System.out.println(newtemp[i]); } } public static Object arrayinc(Object obj,int len){ Class<?>arr=obj.getClass().getComponentType(); Object newArr=Array.newInstance(arr, len); return newArr; } }
4.反射機制的應用場合:
大家不禁會這樣問,什么時候能夠使用到反射機制呢,並且它的應用場合是什么...並且它的優勢到底在哪里呢?
package Fanshefactory; interface factory_1{ public abstract void show(); } class A implements factory_1{ public void show(){ System.out.println("A"); } } class B implements factory_1{ public void show(){ System.out.println("B"); } } /* * 當增添類去實現接口的時候,我們就需要改變factory,當我們添加的類過多的時候 * 我們的修改量就會很大...可以使用反射進行解決... * */ class Factory{ public static factory_1 getInstance(String fname){ factory_1 f=null; if("A".equals(fname)){ f=new A(); } if("B".equals(fname)){ f=new B(); } return f; } } public class Fanshe_factory_1 { public static void main(String[] args) { // TODO Auto-generated method stub factory_1 f=Factory.getInstance("A"); f.show(); } } package Fanshe_factory1; interface factory_2{ public abstract void show(); } class AA implements factory_2{ public void show(){ System.out.println("A"); } } class BB implements factory_2{ public void show(){ System.out.println("B"); } } class Factory_1{ public static factory_2 getInstance(String fname){ factory_2 f=null; try { Class cla=Class.forName(fname); f=(factory_2)cla.newInstance(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return f; } } public class Fanshe_factory_2 { public static void main(String[] args) throws Exception{ // TODO Auto-generated method stub factory_2 f=Factory_1.getInstance("Fanshe_factory1.AA"); if(f!=null){ f.show(); } } }
這兩個代碼形成對比,一個是未使用反射機制的工廠模式,一個是使用了反射機制的工廠模式...估計大家看完沒什么過多的感覺...但是這里比如說我們要添加更多的類去實現我們暴露出的接口,那么我們Factory類里的判斷部分也要進行大量的修改...這么做會代碼冗余...那么用了反射的我們就沒必要進行修改了,因為我們是使用class實例化的對象,class是字節碼文件,無論類如何變,這個字節碼文件只有一個,那么這樣就可以直接實例化對象....我們沒必要進行一個一個判斷了....這就是反射的優勢,在一定程度上解決代碼冗余,並且可以動態的去獲取類的信息...這樣我們就感覺java就像動態語言一樣..因此反射機制是很強大的一個東西...
