以試題為入手點:
什么是反射機制?
簡單說,反射機制指的是程序在運行時能夠獲取自身的信息与斤。在java中,只要給定類的名字荚恶,那么就可以通過反射機制來獲得類的所有信息。java反射機制提供了什么功能磷支?
- 在運行時能夠判斷任意一個對象所屬的類
- 在運行時構(gòu)造任意一個類的對象
- 在運行時判斷任意一個類所具有的成員變量和方法
- 在運行時調(diào)用任一對象的方法
- 在運行時創(chuàng)建新類對象
哪里用到反射機制谒撼?
jdbc中有一行代碼:Class.forName('com.MySQL.jdbc.Driver.class').newInstance();那個時候只知道生成驅(qū)動對象實例,后來才知道雾狈,這就是反射廓潜,現(xiàn)在很多框架都用到反射機制,hibernate善榛,struts都是用反射機制實現(xiàn)的辩蛋。反射機制的優(yōu)缺點?
- 靜態(tài)編譯:在編譯時確定類型移盆,綁定對象悼院,即通過
- 動態(tài)編譯:運行時確定類型,綁定對象咒循。動態(tài)編譯最大限度的發(fā)揮了java的靈活性据途,體現(xiàn)了多態(tài)的應(yīng)用,有利于降低類之間的耦合性叙甸。
一句話颖医,反射機制的優(yōu)點就是可以實現(xiàn)動態(tài)創(chuàng)建對象和編譯,體現(xiàn)出很大的靈活性裆蒸,特別是在J2EE的開發(fā)中它的靈活性就表現(xiàn)的十分明顯熔萧。比如,一個大型的軟件,不可能一次就把把它設(shè)計的很完美佛致,當(dāng)這個程序編譯后遂赠,發(fā)布了,當(dāng)發(fā)現(xiàn)需要更新某些功能時晌杰,我們不可能要用戶把以前的卸載跷睦,再重新安裝新的版本,假如這樣的話,這個軟件肯定是沒有多少人用的。采用靜態(tài)的話碧信,需要把整個程序重新編譯一次才可以實現(xiàn)功能的更新朽色,而采用反射機制的話,它就可以不用卸載雏吭,只需要在運行時才動態(tài)的創(chuàng)建和編譯,就可以實現(xiàn)該功能。
它的缺點是對性能有影響层玲。使用反射基本上是一種解釋操作,我們可以告訴JVM反症,我們希望做什么并且它滿足我們的要求辛块。這類操作總是慢于只直接執(zhí)行相同的操作。
- 動態(tài)加載類
- 編譯時加載類是靜態(tài)加載類铅碍,new 創(chuàng)建對象是靜態(tài)加載類润绵,在編譯時刻就需要加載所有可用使用到的類,如果有一個用不了胞谈,那么整個文件都無法通過編譯
- 運行時加載類是動態(tài)加載類尘盼。Class c = Class.forName("類的全名"),不僅表示了類的類型烦绳,還表示了動態(tài)加載類卿捎,編譯不會報錯,在運行時才會加載径密,使用接口標(biāo)準(zhǔn)能更方便動態(tài)加載類的實現(xiàn)午阵。功能性的類盡量使用動態(tài)加載,而不用靜態(tài)加載睹晒。
- 很多軟件比如QQ,360的在線升級趟庄,并不需要重新編譯文件,只是動態(tài)的加載新的東西
- 如何使用java的反射?
- a. 通過一個全限類名創(chuàng)建一個對象
- Class.forName("全限類名"); 例如:com.mysql.jdbc.Driver Driver類已經(jīng)被加載到 jvm中伪很,并且完成了類的初始化工作就行了 //可以在未知對象情況下使用戚啥,可能會有一個ClassNotFoundException異常
- 類名.class; 獲取Class<?> clz 對象 //實際告訴我們?nèi)魏我粋€類都有一個隱含的靜態(tài)成員變量class(知道類名時用)
- 對象.getClass(); //已知該類的對象通過getClass方法(知道對象時用)
- b. 獲取構(gòu)造器對象锉试,通過構(gòu)造器new出一個對象
- Clazz.getConstructor([String.class]);
- Con.newInstance([參數(shù)]);
- c. 通過class對象創(chuàng)建一個實例對象(就相當(dāng)與new類名()無參構(gòu)造器)
- Clazz.newInstance();
- d. 通過class對象獲得一個屬性對象
- Field c=clz.getFields():獲得某個類的所有的公共(public)的字段猫十,包括父類中的字段。
- Field c=clz.getDeclaredFields():獲得某個類的所有聲明的字段,即包括public拖云、private和proteced贷笛,但是不包括父類的申明字段 e.
- e、通過class對象獲得一個方法對象
- Clazz.getMethod("方法名",class…..parameaType);(只能獲取公共的)
- Clazz.getDeclareMethod("方法名");(獲取任意修飾的方法宙项,不能執(zhí)行私有)
- M.setAccessible(true);(讓私有的方法可以執(zhí)行)
- f乏苦、 讓方法執(zhí)行
- Method.invoke(obj實例對象,obj可變參數(shù));-----(是有返回值的)
- 獲取方法信息
- 1、基本的數(shù)據(jù)類型尤筐,void關(guān)鍵字都存在類類型
1 Class c1 =int.class;//int的類類型
2 Class c2 =String.class;//String類的類類型汇荐,可以理解為編譯生成的那個String.class字節(jié)碼文件,
3 //當(dāng)然盆繁,這并不是官方的說法
4 Class c3 =double.class;
5 Class c4 =Double.class;
6 Class c5 =void.class;
- 2掀淘、Class類的基本API操作
/**
* 打印類的信息,包括類的成員函數(shù)油昂,成員變量
* @param obj 該對象所屬類的信息
*/
publicstaticvoid printClassMessage(Object obj){
//要獲取類的信息革娄,首先要獲取類的類類型
Class c = obj.getClass();//傳遞的是哪個子類的對象,c就是該子類的類類型
//獲取類的名稱
System.out.println("累的名稱是:"+c.getName());
/*
* Method類冕碟,方法的對象
* 一個成員方法就是一個Method對象
* getMethods()方法獲取的是所有的public的函數(shù)拦惋,包括父類繼承而來的
* getDeclaredMethods()獲取的是多有該類自己聲明的方法,不問訪問權(quán)限
*/
Method[] ms = c.getMethods();//c.getDeclaredMethods();
for(int i =0; i < ms.length; i++){
//得到方法的返回值類型的類類型
Class retrunType = ms[i].getReturnType();
System.out.print(retrunType.getName()+" ");
//得到方法的名稱
System.out.print(ms[i].getName()+"(");
//獲取的參數(shù)類型--->得到的是參數(shù)列表的類型的類類型
Class[] paraTypes = ms[i].getParameterTypes();
for(Class class1 : paraTypes){
System.out.print(class1.getName()+",");
}
System.out.println(")");
}
}
8.獲取成員變量構(gòu)造函數(shù)信息
/**
* 成員變量也是對象鸣哀,是java.lang.reflect.Field這個類的的對象
* Field類封裝了關(guān)于成員變量的操作
* getFields()方法獲取的是所有public的成員變量的信息
* getDeclareFields()方法獲取的是該類自己聲明的成員變量的信息
*/
Field[] fs = c.getDeclaredFields();
for(Field field : fs){
//得到成員變量的類型的類類型
Class fieldType = field.getType();
String typeName = fieldType.getName();
//得到成員變量的名稱
String fieldName = field.getName();
System.out.print(typeName+" "+fieldName);
}
/**
* 構(gòu)造函數(shù)也是對象
* java.lang.Constructor中封裝了構(gòu)造函數(shù)的信息
* getConstructor()方法獲取所有的public的構(gòu)造函數(shù)
* getDeclaredConstructors得到所有的構(gòu)造函數(shù)
*/
Constructor[] cs = c.getDeclaredConstructors();
for(Constructor constructor : cs){
System.out.print(constructor.getName()+"(");
//獲取構(gòu)造函數(shù)的參數(shù)列表---》得到的是參數(shù)雷彪的類類型
Class[] paramTypes = constructor.getParameterTypes();
for(Class class1 : paramTypes){
System.out.print(class1.getName()+",");
}
System.out.println(")");
}
9.方法反射的基本操作
1架忌、如何獲取某個方法
方法的名稱和方法的參數(shù)列表才能唯一決定某個方法。Method m = c.getDeclaredMethod("方法名"我衬,可變參數(shù)列表(參數(shù)類型.class))
2、方法的反射操作
m.invoke(對象饰恕,參數(shù)列表)挠羔,方法如果沒有返回值,返回null埋嵌,如果有返回值返回Object類型破加,然后再強制類型轉(zhuǎn)換為原函數(shù)的返回值類型
10.通過反射了解集合泛型的本質(zhì)
ArrayList list1 =new ArrayList();
ArrayList<String> list2 =new ArrayList<String>();
Class c1 = list1.getClass();
Class c2 = list2.getClass();
System.out.println(c1==c2);//結(jié)果為true,為什么雹嗦?范舀?
結(jié)果分析:因為反射的操作都是編譯之后的操作,也就是運行時的操作了罪,c1==c2返回true锭环,說明編譯之后集合的泛型是去泛型化的。那么我們就可以理解為泊藕,Java集合中的泛型辅辩,是用于防止錯誤類型元素輸入的,比如在list2中我們add一個int,add(10)就會編譯報錯玫锋,那么這個泛型就可以只在編譯階段有效蛾茉,通過了編譯階段,泛型就不存在了撩鹿∏妫可以驗證,我們繞過編譯节沦,用反射動態(tài)的在list2中add一個int是可以成功的键思,只是這時因為list2中存儲了多個不同類型的數(shù)據(jù)(String型,和int型)散劫,就不能用for-each來遍歷了稚机,會拋出類型轉(zhuǎn)換錯誤異常ClassCastException