一、什么是反射? 在運行狀態(tài)中,對于任意一個類,都能夠獲取到這個類的所有屬性和方法,對于任意一個對象,都能夠調(diào)用它的任意一個方法和屬性(包括私有的方法和屬性),這種動態(tài)獲取的信息以及動態(tài)調(diào)用對象的方法的功能就稱為java語言的反射機制。通俗點講,通過反射,該類對我們來說是完全透明的,想要獲取任何東西都可以。 想要使用反射機制,就必須要先獲取到該類的字節(jié)碼文件對象(.class),通過字節(jié)碼文件對象,就能夠通過該類中的方法獲取到我們想要的所有信息(方法,屬性,類名,父類名,實現(xiàn)的所有接口等等),每一個類對應(yīng)著一個字節(jié)碼文件也就對應(yīng)著一個Class類型的對象,也就是字節(jié)碼文件對象。 獲取字節(jié)碼文件對象的三種方式。 1 Class clazz1 = Class.forName('全限定類名') 通過Class類中的靜態(tài)方法forName,直接獲取到一個類的字節(jié)碼文件對象,此時該類還是源文件階段,并沒有變?yōu)樽止?jié)碼文件。 2 Class clazz2 = Person.class 當(dāng)類被加載成.class文件時,此時Person類變成了.class,在獲取該字節(jié)碼文件對象,也就是獲取自己, 該類處于字節(jié)碼階段。 3 Class clazz3 = p.getClass() 通過類的實例獲取該類的字節(jié)碼文件對象,該類處于創(chuàng)建對象階段 有了字節(jié)碼文件對象才能獲得類中所有的信息,我們在使用反射獲取信息時,也要考慮使用上面哪種方式獲取字節(jié)碼對象合理,視不同情況而定。下面介紹Class類的功能。 二、反射機制能夠獲取哪些信息 2.1 通過反射獲取類實例信息 //獲取字節(jié)碼文件 Class clazz = Class.forName('com.demo.user'); //創(chuàng)建User實例,這里是通過User的無參構(gòu)造函數(shù)來創(chuàng)建的 User user = (User) clazz1.newInstance(); 2.2 獲取指定構(gòu)造器方法 2.1說明了通過無參構(gòu)造器創(chuàng)建實例的方法,那如果只有有參的構(gòu)造器該如何創(chuàng)建實例呢?
2.3 獲取全部構(gòu)造方法 Class clazz = Class.forName('com.demo.User'); // 獲取所有構(gòu)造方法 Constructor[] constructors = clazz.getConstructors(); // 遍歷所有構(gòu)造方法 for(int i = 0; i < constructors.length; i++) { // 獲取每個構(gòu)造函數(shù)中得參數(shù)類型字節(jié)碼對象。 Class[] parameterTypes = constructors[i].getParameterTypes(); System.out.println('第'+i+'個構(gòu)造函數(shù)'); for(int j = 0; j < parameterTypes.length; j++) { //獲取構(gòu)造函數(shù)中參數(shù)類型 System.out.print(parameterTypes[j].getName()+','); } } 2.4 獲取類得所有成員變量 (1)、獲取指定成員變量
(2)、獲取全部成員變量 Class clazz = Class.forName('com.demo.User'); User user = (User) clazz.newInstance(); user.setId(13); // 賦值操作 user.setName('寶寶'); // 賦值操作 Field[] fields = clazz.getDeclaredFields(); // 將私有的屬性也一并獲得 for(int i = 0; i<fields.length; i++) { fields[i].setAccessible(true); // 將屬性(尤指私有屬性)的權(quán)限打開 System.out.println(fields[i].get(user)); //獲取成員變量的值 } 2.5 獲得方法并使用
2.6 獲取所有方法 Method[] methods = clazz.getDeclaredMethods(); User user = (User) clazz.newInstance(); for(Method method : methods) { method.setAccessible(true); System.out.ptintln(method.getName()); // 獲得方法的參數(shù),又回到了之前的代碼 Class<?> parameterTypes = method.getParameterTypes(); for(int j=0; j<parameterTypes.length; j++) { // 獲取構(gòu)造函數(shù)中的參數(shù)類型 System.out.print(parameterTypes[j].getName()+','); } } 2.7 獲得該類的所有接口 Class[] getInterfaces():確定此對象所表示的類或接口實現(xiàn)的接口 返回值:接口的字節(jié)碼文件對象的數(shù)組 2.8 獲取指定資源的輸入流 InputStream getResourceAsStream(String name) return:一個 InputStream 對象;如果找不到帶有該名稱的資源,則返回 null 參數(shù):所需資源的名稱,如果以'/'開始,則絕對資源名為'/'后面的一部分。 2.9 動態(tài)代理的概述和實現(xiàn) 動態(tài)代理:一種設(shè)計模式,其非常簡單,很容易理解,你自己可以做這件事,但是覺得自己做非常麻煩或者不方便,所以就叫一個另一個人(代理)來幫你做這個事情,而你就不用管了,這就是動態(tài)代理。舉個例子,買火車票叫人代買。 在程序運行過程中產(chǎn)生的這個對象,而程序運行過程中產(chǎn)生對象其實就是我們剛才反射講解的內(nèi)容,所以,動態(tài)代理其實就是通過反射來生成一個代理 在Java中java.lang.reflect包下提供了一個Proxy類和一個InvocationHandler接口,通過使用這個類和接口就可以生成動態(tài)代理對象。JDK提供的代理只能針對接口做代理。我們有更強大的代理cglib,Proxy類中的方法創(chuàng)建動態(tài)代理類對象分三步,但是注意JDK提供的代理只能針對接口做代理,也就是下面的第二步返回的必須要是一個接口。 1、new出代理對象,通過實現(xiàn)InvacationHandler接口,然后new出代理對象來。 2、通過Proxy類中的靜態(tài)方法newProxyInstance,來將代理對象假裝成那個被代理的對象,也就是如果叫人幫我們代買火車票一樣,那個代理就假裝成我們自己本人 3、執(zhí)行方法,代理成功 將代理對象中的內(nèi)容進(jìn)行實現(xiàn)
public interface Student{
public void login(); public void submit(); }
MyInvocationHandler m = new MyInvocationHandler(si); Student s = (Student) Proxy.newProxyInstance(si.getClass().getClassLoader(), si.getClass().getInterfaces(), m); s.login(); s.submit(); 注意newProxyInstance的三個參數(shù),第一個,類加載器,第二個被代理對象的接口,第三個代理對象?! ?/p> 2.10 還有很多方法,比如獲得類加載器,等等 具體還需要別的,就通過查看API文檔來解決。 三、反射機制的應(yīng)用是咧 3.1 利用反射,在泛型為int的arryaList集合中存放一個String類型的對象 原理:集合中的泛型只在編譯器有效,而到了運行期,泛型則會失效
四、總結(jié) 在我們?nèi)粘i_發(fā)中,反射用到的比較少,或者即使碰到也不知道這里是用反射實現(xiàn)的,了解反射的實現(xiàn)更利于我們寫代碼,這篇文章只介紹了反射的基本知識,更多使用場景在實際開發(fā)中遇到會更加好理解。 |
|