天天看點

反射建立對象_淺析JAVA中的反射機制及對Servlet的優化

今天來聊聊java中的反射機制,工作以後發現很多東西動不動就要使用反射或者動态代理,如果不能很好的了解反射,那麼對于動态代理等一些重要的設計模式就會有種不夠通透的感覺。

所謂的反射,就是在運作狀态中,對于任意一個類,都能夠擷取到這個類的所有屬性和方法,對于任意一個對象,都能夠調用它的任意一個方法和屬性(包括私有的方法和屬性),這種動态擷取的資訊以及動态調用對象的方法的功能就稱為java語言的反射機制。通俗點講,通過反射,該類對我們來說是完全透明的,想要擷取任何東西都可以-----很強大。

    想要使用反射機制,就必須要先擷取到該類的位元組碼檔案對象(.class),通過位元組碼檔案對象,就能夠通過該類中的方法擷取到我們想要的所有資訊(方法,屬性,類名,父類名,實作的所有接口等等),每一個類對應着一個位元組碼檔案也就對應着一個Class類型的對象,也就是位元組碼檔案對象。

    擷取位元組碼檔案對象的三種方式。

       1、Class clazz1 = Class.forName("全限定類名");  //通過Class類中的靜态方法forName,直接擷取到一個類的位元組碼檔案對象,此時該類還是源檔案階段,并沒有變為位元組碼檔案。

       2、Class clazz2 = Person.class;    //當類被加載成.class檔案時,此時Person類變成了.class,在擷取該位元組碼檔案對象,也就是擷取自己, 該類處于位元組碼階段。

       3、Person p = new Preson();

Class clazz3 = p.getClass();    //通過類的執行個體擷取該類的位元組碼檔案對象,該類處于建立對象階段 

  有了位元組碼檔案對象才能獲得類中所有的資訊,我們在使用反射擷取資訊時,也要考慮使用上面哪種方式擷取位元組碼對象合理,視不同情況而定。下面介紹Class類的功能

反射機制能夠獲得的資訊:

1.1通過位元組碼對象建立執行個體對象:

//如果user類還沒有加載,那麼可以在源檔案階段擷取其位元組碼檔案對象

Class clazz1 = Class.formName("Reflect.User");

//建立User執行個體,這裡通過User的無參構造方法來建立對象

User user = (User)clazz1.newInstance();

//然後通過user對象就可以擷取我們想要的資訊

1.2擷取指定構造器方法,constructor沒有無參構造方法:

//擷取位元組碼檔案

Class clazz1 = Class.forName("Reflect.User");

//先擷取有參構造器,parameterType:表示參數清單,有多少就要寫多少,如果不寫,預設調用無參構造方法

Construnctor con = clazz1.getConstructor(int.class,String.Class);

//通過構造器來執行個體化對象,将實際的參數傳進去

User user = (User)con.newInstance(12,"小明");

總結上面建立執行個體對象:該類無參的構造方法來是使用該Class類的newInstance()方法來建立對象的, 如果一個類沒有無參的構造函數, 就不能這樣建立了。

這時候可以使用getConstructor(String.class,int.class)方法擷取一個指定的構造函數然後再調用Constructor類的newInstance("張三",20)方法建立對象擷取全部構造方法。

Class clazz1 = Class.forName("Reflect.User");

//擷取所有的構造方法

Constructor[] con = clazz1.getConstructors();

//for循環周遊

for(int i=0;i<con.length;i++){

//擷取每個構造函數中的參數類型位元組碼對象

Class[] parameterTypes = con[i].getParameterTypes();

syso("第"+i+"個構造函數");

for(int j =0;j<paraparameterTypes.length;j++){

//擷取構造函數中的參數類型

syso(paraparameterTypes[j].getName()+",");

}

}

1.3擷取成員變量并使用---Filed對象

擷取指定成員變量

反射建立對象_淺析JAVA中的反射機制及對Servlet的優化

Class.getField(String)方法可以擷取類中的指定字段(可見的), 如果是私有的可以使用getDeclaedField("name")方法擷取,通過set(obj, "李四")方法可以設定指定對象上該字段的值, 如果是私有的需要先調用setAccessible(true)設定通路權限,用擷取的指定的字段調用get(obj)可以擷取指定對象中該字段的值。

1.4獲得方法并使用 Method

反射建立對象_淺析JAVA中的反射機制及對Servlet的優化

Class.getMethod(String, Class...) 和 Class.getDeclaredMethod(String, Class...)方法可以擷取類中的指定方法,如果為私有方法,則需要打開一個權限。setAccessible(true);用invoke(Object, Object...)可以調用該方法,跟上面同理,也能一次性獲得所有的方法:

反射建立對象_淺析JAVA中的反射機制及對Servlet的優化

1.5、獲得該類的所有接口

   Class[] getInterfaces():确定此對象所表示的類或接口實作的接口

   傳回值:接口的位元組碼檔案對象的數組

1.6、擷取指定資源的輸入流

   InputStream getResourceAsStream(String name)  

   return:一個 InputStream 對象;如果找不到帶有該名稱的資源,則傳回 null

   參數:所需資源的名稱,如果以"/"開始,則絕對資源名為"/"後面的一部分。

上面講了一些反射的概念和方法,但是在我們開發中,經常會出現這樣的情況,就是随着業務邏輯的不斷增多,每寫一個功能時,就需要寫一個對應的servlet,最後就會導緻Servlet異常的臃腫。下面來說說如何在servlet中使用反射對其進行優化:

每次從頁面傳過來一個參數,method="xxx"; 然後編寫一個Servlet,獲得其參數method的值,進行判斷,如果是add,則調用add方法,如果是delete,則調用delete方法,這樣就可以寫在一個servlet中實作所有的功能了。

反射建立對象_淺析JAVA中的反射機制及對Servlet的優化

編寫一個BaseServlet繼承HttpServlet,這是一個通用的BaseServlet,這裡需要我們明白servlet的生命周期,也就是service方法,因為是servlet,是以在通路的時候,會經過service方法,而子類MyServlet001中并沒有,是以就到父類BaseServlet中找,發現有,然後擷取參數即知道了需要調用什麼方法,因為方法的編寫都在子類中,是以通過反射,擷取到子類中對應的方法并運作,其中需要注意的是this這個參數在BaseServlet中的用法,需要了解它,才能了解我們這個程式。

反射建立對象_淺析JAVA中的反射機制及對Servlet的優化

編寫具體實作的方法servlet類。MySerlvet001 extends BaseServlet

反射建立對象_淺析JAVA中的反射機制及對Servlet的優化