實(shí)例分析講解java中的反射機(jī)制
推薦 + 挑錯(cuò) + 收藏(0) + 用戶評(píng)論(0)
動(dòng)態(tài)語言
動(dòng)態(tài)語言,是指程序在運(yùn)行時(shí)可以改變其結(jié)構(gòu):新的函數(shù)可以被引進(jìn),已有的函數(shù)可以被刪除等在結(jié)構(gòu)上的變化。比如眾所周知的ECMA(Java)便是一個(gè)動(dòng)態(tài)語言。除此之外如Ruby、Python等也都屬于動(dòng)態(tài)語言,而C、C++等語言則不屬于動(dòng)態(tài)語言。(引自: 百度百科)
var execString = “alert(Math.floor(Math.random()*10));”; eval( execString);Class 反射機(jī)制
指的是可以于運(yùn)行時(shí)加載,探知和使用編譯期間完全未知的類。
程序在運(yùn)行狀態(tài)中, 可以動(dòng)態(tài)加載一個(gè)只有名稱的類, 對(duì)于任意一個(gè)已經(jīng)加載的類,都能夠知道這個(gè)類的所有屬性和方法; 對(duì)于任意一個(gè)對(duì)象,都能調(diào)用他的任意一個(gè)方法和屬性;
加載完類之后, 在堆內(nèi)存中會(huì)產(chǎn)生一個(gè)Class
類型的對(duì)象(一個(gè)類只有一個(gè)Class對(duì)象), 這個(gè)對(duì)象包含了完整的類的結(jié)構(gòu)信息,而且這個(gè)Class對(duì)象就像一面鏡子,透過這個(gè)鏡子看到類的結(jié)構(gòu),所以被稱之為:反射。
Instances of the class Class represent classes and interfaces in a running Java application. An enum is a kind of class and an annotation is a kind of interface. Every array also belongs to a class that is reflected as a Class object that is shared by all arrays with the same element type and number of dimensions(維度)。 The primitive Java types (boolean, byte, char, short, int, long, float, anddouble), and the keyword void are also represented as Class objects.
- 每個(gè)類被加載進(jìn)入內(nèi)存之后,系統(tǒng)就會(huì)為該類生成一個(gè)對(duì)應(yīng)的java.lang.Class
對(duì)象,通過該Class
對(duì)象就可以訪問到JVM中的這個(gè)類。
Class對(duì)象的獲取
- 對(duì)象的getClass()方法;
- 類的.class(最安全/性能最好)屬性;
- 運(yùn)用Class.forName(String className)動(dòng)態(tài)加載類,className需要是類的全限定名(最常用)。
從Class中獲取信息
Class類提供了大量的實(shí)例方法來獲取該Class對(duì)象所對(duì)應(yīng)的詳細(xì)信息,Class類大致包含如下方法,其中每個(gè)方法都包含多個(gè)重載版本,因此我們只是做簡(jiǎn)單的介紹,詳細(xì)請(qǐng)參考JDK文檔
獲取類內(nèi)信息
一些判斷類本身信息的方法
使用反射生成并操作對(duì)象:
Method Constructor Field這些類都實(shí)現(xiàn)了java.lang.reflect.Member接口,程序可以通過Method對(duì)象來執(zhí)行相應(yīng)的方法,通過Constructor對(duì)象來調(diào)用對(duì)應(yīng)的構(gòu)造器創(chuàng)建實(shí)例,通過Filed對(duì)象直接訪問和修改對(duì)象的成員變量值。
創(chuàng)建對(duì)象
通過反射來生成對(duì)象的方式有兩種:
- 使用Class對(duì)象的newInstance()方法來創(chuàng)建該Class對(duì)象對(duì)應(yīng)類的實(shí)例(這種方式要求該Class對(duì)象的對(duì)應(yīng)類有默認(rèn)構(gòu)造器)。
- 先使用Class對(duì)象獲取指定的Constructor對(duì)象, 再調(diào)用Constructor對(duì)象的newInstance()方法來創(chuàng)建該Class對(duì)象對(duì)應(yīng)類的實(shí)例(通過這種方式可以選擇指定的構(gòu)造器來創(chuàng)建實(shí)例)。
通過第一種方式來創(chuàng)建對(duì)象比較常見, 像Spring這種框架都需要根據(jù)配置文件(如applicationContext.xml)信息來創(chuàng)建Java對(duì)象,從配置文件中讀取的只是某個(gè)類的全限定名字符串,程序需要根據(jù)該字符串來創(chuàng)建對(duì)應(yīng)的實(shí)例,就必須使用默認(rèn)的構(gòu)造器來反射對(duì)象。下面我們就模擬Spring實(shí)現(xiàn)一個(gè)簡(jiǎn)單的對(duì)象池, 該對(duì)象池會(huì)根據(jù)文件讀取key-value對(duì), 然后創(chuàng)建這些對(duì)象, 并放入Map中。
配置文件
{ “ objects”: [ { “id”: “id1”, “class”: “com.fq.domain.User”}, { “id”: “id2”, “class”:“com.fq.domain.Bean”} ] }
ObjectPool
/** * Created by jifang on 15/12/31. */publicclassObjectPool{privateMap《String, Object》 pool; privateObjectPool(Map《String, Object》 pool) { this.pool = pool; } privatestaticObjectgetInstance(String className) throwsClassNotFoundException, IllegalAccessException, InstantiationException { returnClass.forName(className).newInstance(); }privatestaticJSONArray getObjects(String config) throwsIOException { Reader reader =newInputStreamReader(ClassLoader.getSystemResourceAsStream(config));returnJSONObject.parseObject(CharStreams.toString(reader)).getJSONArray( “objects”); } // 根據(jù)指定的JSON配置文件來初始化對(duì)象池publicstaticObjectPool init(String config) {try{ JSONArray objects = getObjects(config); ObjectPool pool = newObjectPool(newHashMap《String, Object》()); if(objects != null&& objects.size() != 0) { for( inti = 0; i 《 objects.size(); ++i) { JSONObject object = objects.getJSONObject(i); if(object == null|| object.size() == 0) { continue; } String id = object.getString( “id”); String className = object.getString( “class”); pool.putObject(id, getInstance(className)); } } returnpool; }catch(IOException | ClassNotFoundException | InstantiationException | IllegalAccessException e) { thrownewRuntimeException(e); } } publicObjectgetObject(String id) { returnpool.get(id); } publicvoidputObject(String id, Object object) { pool.put(id, object); } publicvoidclear() { pool.clear(); } }
Client
/** * Java學(xué)習(xí)交流QQ群:589809992 我們一起學(xué)Java! */publicclassClient{@Testpublicvoidclient() { ObjectPool pool = ObjectPool.init(“config.json”); User user = (User) pool.getObject( “id1”); System.out.println(user); Bean bean = (Bean) pool.getObject( “id2”); System.out.println(bean); } }
User
publicclass User { privateint id; privateStringname; privateStringpassword; publicint getId() { returnid; } publicvoidsetId( Integerid) { this .id =id; } publicStringgetName() { returnname; } publicvoidsetName( Stringname) { this .name =name; } publicStringgetPassword() {returnpassword; } publicvoidsetPassword( Stringpassword) { this .password =password; } @Override publicStringtoString() { return“User{”+“id=”+id +“, name=‘”+name +’/‘’ + “, password=‘” + password + ’/‘’ + ‘}’; } }
Bean
publicclass Bean { privateBoolean usefull; privateBigDecimal rate; privateStringname;publicBoolean getUsefull() { returnusefull; } publicvoidsetUsefull(Boolean usefull) { this.usefull =usefull; } publicBigDecimal getRate() { returnrate; } publicvoidsetRate(BigDecimal rate) { this .rate =rate; } publicStringgetName() { returnname; } publicvoidsetName(Stringname) { this .name =name; } @Override publicStringtoString() {return“Bean{”+“usefull=”+usefull +“, rate=”+rate +“, name=‘”+name +’/‘’ + ‘} ’; } }
注意: 需要在pom.xml中添加如下依賴:
《dependency》《groupId》com.alibaba 《/groupId》《artifactId》fastjson 《/artifactId》《version》1.2.7 《/version》《/dependency》《dependency》《groupId》com.google.guava《/groupId》《artifactId》guava 《/artifactId》《version》18.0 《/version》《/dependency》
調(diào)用方法
當(dāng)獲取到某個(gè)類對(duì)應(yīng)的Class對(duì)象之后, 就可以通過該Class對(duì)象getMethod來獲取一個(gè)Method數(shù)組或Method對(duì)象。每個(gè)Method對(duì)象對(duì)應(yīng)一個(gè)方法,在獲得Method對(duì)象之后,就可以通過調(diào)用invoke方法來調(diào)用該Method對(duì)象對(duì)應(yīng)的方法。
@CallerSensitive public Object invoke(Object obj, Object.。。 args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { 。。.}
下面我們對(duì)上面的對(duì)象池加強(qiáng):可以看到Client獲取到的對(duì)象的成員變量全都是默認(rèn)值,既然我們已經(jīng)使用了JSON這么優(yōu)秀的工具,我們又學(xué)習(xí)了動(dòng)態(tài)調(diào)用對(duì)象的方法,那么我們就通過配置文件來給對(duì)象設(shè)置值(在對(duì)象創(chuàng)建時(shí)), 新的配置文件形式如下:
{ “ objects”: [ { “id”: “id1”, “class”: “com.fq.domain.User”, “fields”: [ { “name”: “id”, “value”:101}, { “name”: “name”, “value”: “feiqing”}, { “name”: “password”, “value”:“ICy5YqxZB1uWSwcVLSNLcA==”} ] }, { “id”: “id2”, “class”: “com.fq.domain.Bean”, “fields”:[ { “name”: “usefull”, “value”: true}, { “name”: “rate”, “value”: 3.14}, { “name”: “name”, “value”: “bean-name”} ] }, { “id”: “id3”, “class”: “com.fq.domain.ComplexBean”, “fields”: [ { “name”: “name”, “value”: “complex-bean-name”}, { “name”: “refBean”, “ref”: “id2”} ] } ] }
其中fields代表該Bean所包含的屬性, name為屬性名稱, value為屬性值(屬性類型為JSON支持的類型), ref代表引用一個(gè)對(duì)象(也就是屬性類型為Object,但是一定要引用一個(gè)已經(jīng)存在了的對(duì)象)
/** *@authorjifang *@since15/12/31下午4:00 */publicclassObjectPool{privateMap《String, Object》 pool; privateObjectPool(Map《String, Object》 pool) { this.pool = pool; }privatestaticJSONArray getObjects(String config) throwsIOException { Reader reader =newInputStreamReader(ClassLoader.getSystemResourceAsStream(config));returnJSONObject.parseObject(CharStreams.toString(reader)).getJSONArray( “objects”); } privatestaticObject getInstance(String className, JSONArray fields)throwsClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException { // 配置的ClassClass《?》 clazz = Class.forName(className); // 目標(biāo)Class的實(shí)例對(duì)象Object targetObject = clazz.newInstance(); if(fields != null&& fields.size() != 0) { for( inti = 0; i 《 fields.size(); ++i) { JSONObject field = fields.getJSONObject(i); // 需要設(shè)置的成員變量名String fieldName = field.getString( “name”); // 需要設(shè)置的成員變量的值Object fieldValue; if(field.containsKey(“value”)) { fieldValue = field.get( “value”); } elseif(field.containsKey( “ref”)) { String refBeanId = field.getString( “ref”); fieldValue = OBJECTPOOL.getObject(refBeanId); }else{ thrownewRuntimeException( “neither value nor ref”); } String setterName = “set”+ fieldName.substring( 0, 1).toUpperCase() + fieldName.substring( 1); // 需要設(shè)置的成員變量的setter方法Method setterMethod = clazz.getMethod(setterName, fieldValue.getClass()); // 調(diào)用setter方法將值設(shè)置進(jìn)去setterMethod.invoke(targetObject, fieldValue); } } returntargetObject; } privatestaticObjectPool OBJECTPOOL; // 創(chuàng)建一個(gè)對(duì)象池的實(shí)例(保證是多線程安全的)privatestaticvoidinitSingletonPool() { if(OBJECTPOOL ==null) { synchronized(ObjectPool.class) { if(OBJECTPOOL == null) { OBJECTPOOL =newObjectPool( newConcurrentHashMap《String, Object》()); } } } } // 根據(jù)指定的JSON配置文件來初始化對(duì)象池publicstaticObjectPool init(String config) { // 初始化poolinitSingletonPool(); try{ JSONArray objects = getObjects(config); for( inti = 0; objects != null&& i 《 objects.size(); ++i) { JSONObject object = objects.getJSONObject(i); if(object == null|| object.size() == 0) { continue; } String id = object.getString( “id”); String className = object.getString( “class”); // 初始化bean并放入池中OBJECTPOOL.putObject(id, getInstance(className, object.getJSONArray( “fields”))); }returnOBJECTPOOL; } catch(IOException | ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) { thrownewRuntimeException(e); } } publicObjectgetObject(String id) { returnpool.get(id); } publicvoidputObject(String id, Object object) { pool.put(id, object); } publicvoidclear() { pool.clear(); } }
Client
/** * Java學(xué)習(xí)交流QQ群:589809992 我們一起學(xué)Java! */publicclassClient{@Testpublicvoidclient() { ObjectPool pool = ObjectPool.init(“config.json”); User user = (User) pool.getObject( “id1”); System.out.println(user); Bean bean = (Bean) pool.getObject( “id2”); System.out.println(bean); ComplexBean complexBean = (ComplexBean) pool.getObject( “id3”); System.out.println(complexBean); } }
ComplexBean
publicclass ComplexBean { privateStringname; privateBean refBean;publicStringgetName() { returnname; } publicvoidsetName( Stringname) { this .name=name; } publicBean getRefBean() { returnrefBean; } publicvoidsetRefBean(Bean refBean) { this .refBean =refBean; } @Override publicStringtoString() {return“ComplexBean{”+“name=‘”+name +’/‘’ + “, refBean=” + refBean + ‘} ’; } }
Spring框架就是通過這種方式將成員變量值以及依賴對(duì)象等都放在配置文件中進(jìn)行管理的,從而實(shí)現(xiàn)了較好地解耦(不過Spring是通過XML作為配置文件)。
訪問成員變量
通過Class對(duì)象的的getField()方法可以獲取該類所包含的全部或指定的成員變量Field,F(xiàn)iled提供了如下兩組方法來讀取和設(shè)置成員變量值。
- getXxx(Object obj): 獲取obj對(duì)象的該成員變量的值, 此處的Xxx對(duì)應(yīng)8中基本類型,如果該成員變量的類型是引用類型, 則取消get后面的Xxx;
- setXxx(Object obj, Xxx val): 將obj對(duì)象的該成員變量值設(shè)置成val值。此處的Xxx對(duì)應(yīng)8種基本類型, 如果該成員類型是引用類型, 則取消set后面的Xxx;
注: getDeclaredXxx方法可以獲取所有的成員變量,無論private/public;
/** *@authorjifang *@since16/1/2下午1:00. */publicclassClient{@Testpublicvoidclient()throwsNoSuchFieldException, IllegalAccessException { User user = newUser(); Field idFiled = User.class.getDeclaredField( “id”); setAccessible(idFiled); idFiled.setInt(user,46); Field nameFiled = User.class.getDeclaredField( “name”); setAccessible(nameFiled); nameFiled.set(user, “feiqing”); Field passwordField = User.class.getDeclaredField(“password”); setAccessible(passwordField); passwordField.set(user,“ICy5YqxZB1uWSwcVLSNLcA==”); System.out.println(user); }privatevoidsetAccessible(AccessibleObject object) { object.setAccessible( true); } }使用反射獲取泛型信息
為了通過反射操作泛型以迎合實(shí)際開發(fā)的需要, Java新增了java.lang.reflect.ParameterizedType java.lang.reflect.GenericArrayTypejava.lang.reflect.TypeVariable java.lang.reflect.WildcardType幾種類型來代表不能歸一到Class類型但是又和原始類型同樣重要的類型。
其中, 我們可以使用ParameterizedType來獲取泛型信息。
/** * Java學(xué)習(xí)交流QQ群:589809992 我們一起學(xué)Java! */publicclassClient{privateMap《String, Object》 objectMap; publicvoidtest(Map《String, User》 map, String string) { } publicMap《User, Bean》 test() { returnnull; } /** * 測(cè)試屬性類型 * *@throwsNoSuchFieldException */@TestpublicvoidtestFieldType()throwsNoSuchFieldException { Field field = Client.class.getDeclaredField( “objectMap”); Type gType = field.getGenericType(); // 打印type與generic type的區(qū)別System.out.println(field.getType()); System.out.println(gType); System.out.println(“**************”); if(gType instanceofParameterizedType) { ParameterizedType pType = (ParameterizedType) gType; Type[] types = pType.getActualTypeArguments(); for(Type type : types) { System.out.println(type.toString()); } } } /** * 測(cè)試參數(shù)類型 * *@throwsNoSuchMethodException */@TestpublicvoidtestParamType()throwsNoSuchMethodException { Method testMethod = Client.class.getMethod( “test”, Map.class, String.class); Type[] parameterTypes = testMethod.getGenericParameterTypes(); for(Type type : parameterTypes) { System.out.println( “type -》 ”+ type); if(type instanceofParameterizedType) { Type[] actualTypes = ((ParameterizedType) type).getActualTypeArguments(); for(Type actualType : actualTypes) { System.out.println( “/tactual type -》 ”+ actualType); } } } } /** * 測(cè)試返回值類型 * *@throwsNoSuchMethodException */@TestpublicvoidtestReturnType()throwsNoSuchMethodException { Method testMethod = Client.class.getMethod( “test”); Type returnType = testMethod.getGenericReturnType(); System.out.println( “return type -》 ”+ returnType); if(returnType instanceofParameterizedType) { Type[] actualTypes = ((ParameterizedType) returnType).getActualTypeArguments(); for(Type actualType : actualTypes) { System.out.println( “/tactual type -》 ”+ actualType); } } } }反射性能測(cè)試
Method/Constructor/Field/Element都繼承了AccessibleObject,AccessibleObject類中有一個(gè)setAccessible方法:
public void setAccessible(boolean flag) throws SecurityException { 。。.}
該方法有兩個(gè)作用:
啟用/禁用訪問安全檢查開關(guān):值為true,則指示反射的對(duì)象在使用時(shí)取消Java語言訪問檢查;值為false,則指示應(yīng)該實(shí)施Java語言的訪問檢查;
可以禁止安全檢查, 提高反射的運(yùn)行效率。
/** *@authorjifang *@since15/12/31下午4:53. */ public classTestReflect{ @Before publicvoid testNoneReflect() { User user = newUser(); longstart = System.currentTimeMillis();for( longi = 0; i 《 Integer.MAX_VALUE; ++i) { user.getName(); } longcount = System.currentTimeMillis() - start; System.out.println( “沒有反射, 共消耗 《”+ count + “》 毫秒”); } @Test public void testNotAccess() throwsClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { User user = newUser(); Method method = Class.forName(“com.fq.domain.User”).getMethod( “getName”); longstart = System.currentTimeMillis();for( longi = 0; i 《 Integer.MAX_VALUE; ++i) { method.invoke(user, null); } longcount = System.currentTimeMillis() - start; System.out.println( “沒有訪問權(quán)限, 共消耗 《”+ count +“》 毫秒”); } @After public void testUseAccess() throwsClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { User user = newUser(); Method method = Class.forName(“com.fq.domain.User”).getMethod( “getName”); method.setAccessible( true); longstart = System.currentTimeMillis(); for( longi = 0; i 《 Integer.MAX_VALUE; ++i) { method.invoke(user, null); } longcount = System.currentTimeMillis() - start; System.out.println( “有訪問權(quán)限, 共消耗 《”+ count + “》 毫秒”); } }
執(zhí)行上面程序,在我的機(jī)器上會(huì)有如下結(jié)果:
機(jī)器配置信息如下:
可以看到使用反射會(huì)比直接調(diào)用慢3000毫秒,但是前提是該方法會(huì)執(zhí)行20E+次(而且服務(wù)器的性能也肯定比我的機(jī)器要高),因此在我們的實(shí)際開發(fā)中,其實(shí)是不用擔(dān)心反射機(jī)制帶來的性能消耗的,而且禁用訪問權(quán)限檢查,也會(huì)有性能的提升。
非常好我支持^.^
(0) 0%
不好我反對(duì)
(0) 0%