一.反射進(jìn)階之動(dòng)態(tài)設(shè)置類(lèi)的私有域
"封裝"是Java的三大特性之一,為了能更好保證其封裝性,我們往往需要將域設(shè)置成私有的,
然后通過(guò)提供相對(duì)應(yīng)的set和get方法來(lái)操作這個(gè)域。但是我們?nèi)匀豢梢杂胘ava的反射機(jī)制來(lái)
修改類(lèi)的私有域,由于修改類(lèi)的私有域會(huì)破壞Java"封裝"的特性,故請(qǐng)慎重操作。
主要技術(shù): Field類(lèi)提供有關(guān)類(lèi)或接口的單個(gè)字段的信息,以及對(duì)它的動(dòng)態(tài)訪問(wèn)權(quán)限。 訪問(wèn)的字段可能是一個(gè)類(lèi)(靜態(tài))字段或?qū)嵗侄巍? 常用方法: set(Object obj,Object value)-----: 將指定對(duì)象變量上此Field對(duì)象表示的字段設(shè)置為指定的新值 setBoolean(Object obj,boolean z)-: 將字段使得設(shè)置為指定對(duì)象上的一個(gè)boolean值 setDouble(Object obj,double d)---: 將字段的值設(shè)置為指定對(duì)象上的一個(gè)double值 setInt(Object obj,int i)---------: 將字段的值設(shè)置為指定對(duì)象上的一個(gè)int值 setaccessible(boolean flag)------: 將此對(duì)象的accessible標(biāo)志設(shè)置為指定的布爾值 注:對(duì)于私有域,外部類(lèi)調(diào)用的時(shí)候一定要使用setAccessible()方法將其可見(jiàn)性設(shè)置為true才能設(shè)置新值, 否則將會(huì)拋出異。
--支持知識(shí)共享,轉(zhuǎn)載請(qǐng)標(biāo)注地址"http://m.survivalescaperooms.com/XHJT/p/3922160.html"——和佑博客園,謝謝~~--
實(shí)例代碼:
package com.xhj.reflection_excetion;import java.lang.reflect.Field;/** * 動(dòng)態(tài)修改類(lèi)的私有域 * * @author XIEHEJUN * */public class DynamicChangePRivate { private String userName; private int userAge; private String userAddress; private boolean userGender; public DynamicChangePrivate(String userName, int userAge, String userAddress, boolean userGender) { super(); this.userName = userName; this.userAge = userAge; this.userAddress = userAddress; this.userGender = userGender; } public DynamicChangePrivate() { super(); } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public int getUserAge() { return userAge; } public void setUserAge(int userAge) { this.userAge = userAge; } public String getUserAddress() { return userAddress; } public void setUserAddress(String userAddress) { this.userAddress = userAddress; } public boolean isUserGender() { return userGender; } public void setUserGender(boolean userGender) { this.userGender = userGender; } /** * 根據(jù)獲得的性別的真假,獲取其String值: true--為男 false--為女 * * @param userGender * @return */ public String getGender(boolean userGender) { if (userGender) { return "男"; } else { return "女"; } } public static void main(String[] args) { DynamicChangePrivate user = new DynamicChangePrivate("小黑", 21, "北京路華西小區(qū)3單元446", true); Class<?> clazz = user.getClass(); System.out.println("通過(guò)反射取得的對(duì)象全稱為:" + clazz.getName()); try { // 獲取要修改的類(lèi)的字段名稱 Field userName = clazz.getDeclaredField("userName"); Field userAge = clazz.getDeclaredField("userAge"); Field userAddress = clazz.getDeclaredField("userAddress"); Field userGender = clazz.getDeclaredField("userGender"); // 修改并輸出新舊名稱 System.out.print("原名稱為:" + user.getUserName()); userName.set(user, "曉曉"); System.out.println("/t/t/t修改后的名稱為:" + user.getUserName()); // 修改并輸出新舊年齡 System.out.print("原年齡為:" + user.getUserAge()); userAge.set(user, 24); System.out.println("/t/t/t修改后的年齡為:" + user.getUserAge()); // 修改并輸出新舊地址 System.out.print("原地址為:" + user.getUserAddress()); userAddress.set(user, "石景山八角南里3單元506"); System.out.println("/t修改后的地址為:" + user.getUserAddress()); // 修改并輸出新舊性別 System.out.print("原性別為:" + user.getGender(user.isUserGender())); userGender.set(user, false); System.out.println("/t/t/t修改后的性別為:" + user.getGender(user.isUserGender())); } catch (SecurityException e) { e.printStackTrace(); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } }}
結(jié)果為:

注:任何類(lèi)型的域,都可以通過(guò)set(Object obj, Object value) 方法將指定對(duì)象變量上此 Field 對(duì)象表示的字段設(shè)置為指定的新值。 但是Field本身也提供了其他類(lèi)型相對(duì)應(yīng)的set方法,如 setBoolean(Object obj, boolean z),setDouble(Object obj, double d)
等。另外,通過(guò)Field也可以設(shè)置public.protected域,但一般情況下很少這么設(shè)置,尤其是public域。在這里特別要注意的是:一定
要明確修改的含義,不要輕易的通過(guò)反射來(lái)修改類(lèi)的私有域,因?yàn)檫@破壞了java面向?qū)ο?封裝"的特性。
二、反射進(jìn)階之動(dòng)態(tài)調(diào)用類(lèi)的方法
我們知道Java是一種面向?qū)ο蟮恼Z(yǔ)言,對(duì)他而言,一切都是對(duì)象,因此要調(diào)用類(lèi)的方法,只能通過(guò)建立類(lèi)的對(duì)象來(lái)調(diào)用。當(dāng)然如果是 靜態(tài)的方法,那就可以直接通過(guò)類(lèi)本身來(lái)調(diào)用,而不需要建立類(lèi)的對(duì)象。那么還有沒(méi)有其他可以調(diào)用類(lèi)方法的方式呢??
在Java的反射的機(jī)制中,提供了比較另類(lèi)的調(diào)用方式,既可以根據(jù)需要指定要調(diào)用的方法,而不必在編程時(shí)確定。調(diào)用的方法不僅權(quán)限于public 的,還可以是private的。
Method類(lèi)提供類(lèi)或接口上單獨(dú)某個(gè)方法(以及如何訪問(wèn)該方法)的信息,所反映的方法可能是類(lèi)方法或?qū)嵗椒ǎòǔ橄蠓椒ǎK试S在匹配 要調(diào)用的實(shí)參與底層方法的形參時(shí)進(jìn)行擴(kuò)展轉(zhuǎn)換,但是如果要進(jìn)行收縮轉(zhuǎn)換,則會(huì)拋出"非法參數(shù)異常"--IllegalArgumentExcetion。使用invoke() 可以實(shí)現(xiàn)動(dòng)態(tài)調(diào)用方法: public Object invoke(Object obj,Object...args)throws IllegalArgumentException,IllegalAccessException,InvocationTargetExcetion obj--要調(diào)用的方法的類(lèi)對(duì)象 args--方法調(diào)用的參數(shù) 注:對(duì)于私有域,外部類(lèi)調(diào)用的時(shí)候一定要使用setAccessible,并且設(shè)置為true。 實(shí)例代碼:
package com.xhj.reflection_excetion;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;/** * 實(shí)現(xiàn)動(dòng)態(tài)調(diào)用類(lèi)的方法 * * @author XIEHEJUN * */public class DynamicCallMethod { public static void main(String[] args) { try { // 運(yùn)行時(shí)動(dòng)態(tài)調(diào)用Math的abs()靜態(tài)方法 System.out.println("運(yùn)行時(shí)動(dòng)態(tài)調(diào)用Math的abs()靜態(tài)方法"); Method meth = Math.class.getDeclaredMethod("abs", Integer.TYPE); Integer a = (Integer) meth.invoke(null, new Integer(-1)); System.out.println("-1的絕對(duì)值為:" + a); // 運(yùn)行時(shí)動(dòng)態(tài)調(diào)用String的非靜態(tài)方法contains() System.out.println("運(yùn)行時(shí)動(dòng)態(tài)調(diào)用String的非靜態(tài)方法contains()"); Method strMeth = String.class.getDeclaredMethod("contains", CharSequence.class); boolean str = (Boolean) strMeth.invoke(new String("xhjit"), "xhj"); System.out.println("xhjit中是否包含有xhj——" + str); } catch (SecurityException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (NoSuchMethodException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InvocationTargetException e) { // TODO Auto-generated catch block e.printStackTrace(); } }}結(jié)果為:

三、反射進(jìn)階之動(dòng)態(tài)實(shí)例化類(lèi)
在Java中,要實(shí)例化一個(gè)類(lèi)即創(chuàng)建一個(gè)類(lèi)的對(duì)象,需要通過(guò)構(gòu)造方法來(lái)實(shí)現(xiàn)。但是在Java中還可以使用另外一種方式來(lái)實(shí)現(xiàn),
那就是通過(guò)反射來(lái)實(shí)例化一個(gè)類(lèi)。 Constructor是Java中提供類(lèi)的單個(gè)構(gòu)造方法的信息以及訪問(wèn)權(quán)限的封裝類(lèi)。 它允許在將實(shí)參與帶有底層構(gòu)造方法的形參的newInstance()匹配時(shí)進(jìn)行擴(kuò)展轉(zhuǎn)換, 但是如果發(fā)生收縮轉(zhuǎn)換,則拋出IllegalArgumentExcetion。newInstance()方法可以 使用指定的參數(shù)來(lái)創(chuàng)建對(duì)象: public T newInstance(Object...initargs)throws InstantiationException,IllegalAccessException,
IllegalArgumentException,InvocationTargetException initargs: 將作為變量傳遞給構(gòu)造方法調(diào)用的對(duì)象數(shù)組。 注:對(duì)于私有域,外部類(lèi)調(diào)用的時(shí)候一定要使用setAccessible,并且設(shè)置為true。 代碼實(shí)例為:
package com.xhj.reflection_excetion;import java.io.File;import java.io.FileOutputStream;import java.io.IOException;import java.io.OutputStream;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationTargetException;/** * 動(dòng)態(tài)實(shí)例化類(lèi) * * @author XIEHEJUN * */public class DynamiCreateClass { public static void main(String[] args) { // TODO Auto-generated method stub try { // 動(dòng)態(tài)建立一個(gè)String對(duì)象, Constructor<?> construtor = String.class .getConstructor(String.class); String str = (String) construtor.newInstance("000123"); System.out.println(Integer.parseInt(str)); // 動(dòng)態(tài)建立一個(gè)txt文件,并將上面初始化后的String值寫(xiě)入文件的當(dāng)中 construtor = File.class.getConstructor(String.class); String url = "C://Users//XIEHEJUN//Desktop//XHj.txt"; File file = (File) construtor .newInstance(url); file.createNewFile(); if (file.exists()) { str += "---文件創(chuàng)建成功"; System.out.println(str); OutputStream os = new FileOutputStream(url); os.write(str.getBytes()); os.close(); } else { System.out.println("創(chuàng)建文件失敗"); } } catch (SecurityException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }}結(jié)果為:
控制臺(tái)輸出

生成的文件:

注:在java中有兩種不用new就可以建立類(lèi)對(duì)象的方法,即Class.newInstance()和Constructor.newInstance(); 區(qū)別在于: 前者只能調(diào)用無(wú)參構(gòu)造方法,而后者卻能調(diào)用有參構(gòu)造方法; 且前者需要被調(diào)用的構(gòu)造方法可見(jiàn),后者則在特定情況下運(yùn)行調(diào)用不可見(jiàn)的構(gòu)造方法
四、反射進(jìn)階之自定義可變數(shù)組工具類(lèi)
在Java中,要?jiǎng)?chuàng)建可變數(shù)組可通過(guò)ArraryList類(lèi)來(lái)實(shí)現(xiàn)。除此之外,我們也可以用自定義的方法來(lái)實(shí)現(xiàn)可變數(shù)組。 這里,我們將使用Java的反射機(jī)制實(shí)現(xiàn)一個(gè)工具類(lèi),通過(guò)這個(gè)工具類(lèi),我們就能實(shí)現(xiàn)可變數(shù)組的創(chuàng)建。
主要技術(shù):
Array類(lèi)提供了動(dòng)態(tài)創(chuàng)建和訪問(wèn)Java數(shù)組的方法。它允許在執(zhí)行g(shù)et,set操作期間進(jìn)行擴(kuò)展轉(zhuǎn)換,但若發(fā)生收縮 轉(zhuǎn)換將拋出IllegalArgumentExcetion。為了創(chuàng)建新的數(shù)組對(duì)象,需要使用newInstance()方法,它可以根據(jù)指定 的元素類(lèi)型和長(zhǎng)度創(chuàng)建新的數(shù)組:
public static Object newInstance(Class<?> componentType,int length)throws NegativeArraySizeException
componentType: 表示新數(shù)組的組件類(lèi)型的Class對(duì)象 length: 新數(shù)組的長(zhǎng)度
注:Java 中的數(shù)組不管是幾維的,都是Object類(lèi)型的s
代碼實(shí)例: 可變數(shù)組工具類(lèi)的實(shí)現(xiàn):
package com.xhj.reflection_excetion;import java.lang.reflect.Array;/** * 用反射機(jī)制實(shí)現(xiàn)可變數(shù)組工具類(lèi) * * @author XIEHEJUN * */public class VariableArrayUtil { /** * 增加的數(shù)組長(zhǎng)度值 */ private int addLength; /** * 需要增加長(zhǎng)度的原數(shù)組 */ private Object array; public int getaddLength() { return addLength; } public Object getArray() { return array; } /** * 可變數(shù)組初始化 * @param addLength 需要增加的數(shù)組的長(zhǎng)度 * @param array 需要增加長(zhǎng)度的原數(shù)組 */ public VariableArrayUtil(int addLength, Object array) { super(); this.addLength = addLength; this.array = array; } /** * 可變數(shù)組的實(shí)現(xiàn) * * @return */ public Object newArrary() { Class<?> clazz = array.getClass(); if (clazz.isArray()) { Class<?> type = clazz.getComponentType(); int length = Array.getLength(array); Object new_Array = Array.newInstance(type, length + addLength); System.arraycopy(array, 0, new_Array, 0, length); return new_Array; } return null; }}測(cè)試類(lèi):
package com.xhj.test;import com.xhj.reflection_excetion.VariableArrayUtil;/** * 可變數(shù)組工具類(lèi)的測(cè)試類(lèi) * * @author XIEHEJUN * */public class Test { public static void main(String[] args) { int[] a = new int[10]; System.out.println("原數(shù)組為:"); for (int i = 0; i < 10; i++) { a[i] = i; System.out.print(" " + a[i]); } System.out.println("/n數(shù)組長(zhǎng)度為:" + a.length); VariableArrayUtil util = new VariableArrayUtil(5, (Object) a); int[] b = (int[]) util.newArrary(); System.out .println("==================================================/n" + "更改后的數(shù)組長(zhǎng)度為:" + b.length); for (int i = 10; i < 15; i++) { b[i] = i; } System.out.println("更改后的數(shù)組為:"); for (int i : b) { System.out.print(" " + i); } } }結(jié)果為:

五、反射進(jìn)階之重寫(xiě)toString方法
為了方便輸出對(duì)象,在Object中定義了toString的方法,其默認(rèn)值由類(lèi)名和哈希碼組成。但是很多時(shí)候,為了能更好的滿足我們的需求,
我們都是需要重寫(xiě)這個(gè)方法的。下面我們將利用Java的反射機(jī)制,重寫(xiě)這個(gè)方法,并輸出類(lèi)的相關(guān)信息。
代碼實(shí)例:
package com.xhj.reflection_excetion;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.Method;import java.lang.reflect.Type;/** * 利用反射重寫(xiě)Object的toString方法 * * @author XIEHEJUN * */public class RewriteToString { public String toString(Object obj) { Class<?> clazz = obj.getClass(); // 建立一個(gè)容器用來(lái)存儲(chǔ)類(lèi)的信息 StringBuilder strBuilder = new StringBuilder(); strBuilder.append("以下是類(lèi)的信息:"); // 類(lèi)名 String className = clazz.getSimpleName(); // 包名 Package packageName = clazz.getPackage(); // 公共構(gòu)造方法 Constructor<?>[] constructors = clazz.getConstructors(); // 公共域 Field[] fields = clazz.getFields(); // 接口 Type[] interfaces = clazz.getInterfaces(); // 公共方法 Method[] methods = clazz.getMethods(); strBuilder.append("/n此類(lèi)的簡(jiǎn)單名稱為--" + className); strBuilder.append("/n此類(lèi)的包名為--" + packageName); strBuilder.append("/n此類(lèi)的公共構(gòu)造方法有:"); if (constructors.length > 0) { for (Constructor<?> constructor : constructors) { strBuilder.append("/n/t" + constructor); } } else { strBuilder.append("空"); } strBuilder.append("/n此類(lèi)的公共域有:"); if (fields.length > 0) { for (Field field : fields) { strBuilder.append("/n/t" + field); } } else { strBuilder.append("空"); } strBuilder.append("/n此類(lèi)的接口有:"); if (fields.length > 0) { for (Type type : interfaces) { strBuilder.append("/n/t" + type); } } else { strBuilder.append("空"); } strBuilder.append("/n此類(lèi)的公共方法有:"); if (methods.length > 0) { for (Method method : methods) { strBuilder.append("/n/t" + method); } } else { strBuilder.append("空"); } return strBuilder.toString(); } public static void main(String[] args) { RewriteToString rts = new RewriteToString(); System.out.println(rts.toString(new StringBuilder())); }}結(jié)果為:

注:在實(shí)際開(kāi)發(fā)當(dāng)中,一般而言都是需要重寫(xiě)toString方法的,為了更好的開(kāi)發(fā),可使用Commons Lang 組件提供的工具類(lèi)來(lái)重寫(xiě)此方法。
六、反射進(jìn)階之動(dòng)態(tài)代理
代理是Java中很重要的一個(gè)特性,使用代理可以在程序運(yùn)行時(shí)創(chuàng)建一個(gè)實(shí)現(xiàn)指定接口的新類(lèi)。一般而言,我們只有在程序編譯時(shí)
無(wú)法確定需要使用哪些接口時(shí)才會(huì)使用代理機(jī)制。代理更多的是用在系統(tǒng)的開(kāi)發(fā)上,它可以為工具類(lèi)提供更加靈活的特性。
InvocationHandle 接口是代理實(shí)例的調(diào)用處理程序?qū)崿F(xiàn)的接口。每個(gè)代理實(shí)例都具有一個(gè)關(guān)聯(lián)的調(diào)用處理程序。
對(duì)代理實(shí)例調(diào)用方法時(shí),將對(duì)方法調(diào)用進(jìn)行編碼并將其指派到它的調(diào)用處理程序的invoke()方法:
Object invoke(Object proxy,Method method,Object[]args)throws Throwable proxy:代理類(lèi) method: 代理實(shí)例上要被調(diào)用的方法 args: 代理實(shí)例上方法調(diào)用的參數(shù)數(shù)組
Proxy接口提供了用于創(chuàng)建動(dòng)態(tài)代理類(lèi)和實(shí)例的靜態(tài)方法,它是所有動(dòng)態(tài)代理類(lèi)的父類(lèi)。
獲得一個(gè)指定接口的代理類(lèi)實(shí)例: public static Object newProxyInstance(ClassLoader loader,Class<?> [] interfaces,InvocationHandle h)throws IllegalArgumentException loader:定義代理類(lèi)的類(lèi)加載器 interfaces:代理類(lèi)要實(shí)現(xiàn)的接口列表 h:指派方法調(diào)用的代理處理程序
代碼實(shí)例:
接口:
package com.xhj.reflection_excetion.dynamicProxy.bean;/** * 代理類(lèi)和被代理類(lèi)的共同接口 * * @author XIEHEJUN * */public interface DoBusiness { /** * 商品交易方式 */ public void trading();}被代理類(lèi):
package com.xhj.reflection_excetion.dynamicProxy.service;import com.xhj.reflection_excetion.dynamicProxy.bean.DoBusiness;/** * 被代理類(lèi)--廠家 * * @author XIEHEJUN * */public class Product implements DoBusiness { @Override public void trading() { System.out.println("廠家直銷(xiāo)"); }}代理處理器:
package com.xhj.reflection_excetion.dynamicProxy.proxy;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;/** * 代理處理器--商家代理 * * @author XIEHEJUN * */public class DynamicProxy implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("商家代理定點(diǎn)銷(xiāo)售"); return null; }}測(cè)試類(lèi):
package com.xhj.reflection_excetion.dynamicProxy;import java.lang.reflect.Proxy;import com.xhj.reflection_excetion.dynamicProxy.bean.DoBusiness;import com.xhj.reflection_excetion.dynamicProxy.proxy.DynamicProxy;import com.xhj.reflection_excetion.dynamicProxy.service.Product;public class Test { public static void main(String[] args) { DoBusiness p = new Product(); System.out.print("沒(méi)有啟用代理模式---"); p.trading(); ClassLoader loader = p.getClass().getClassLoader(); p = (DoBusiness) Proxy.newProxyInstance(loader, Product.class.getInterfaces(), new DynamicProxy()); System.out.print("啟用動(dòng)態(tài)代理模式---"); p.trading(); }}結(jié)果為:

注: java動(dòng)態(tài)代理類(lèi)位于java.lang.reflect包下,一般主要涉及到以下兩個(gè)類(lèi):
Interface InvocationHandler:該接口中僅定義了一個(gè)方法Object:invoke(Object obj,Method method, Object[] args)。 在實(shí)際使用時(shí),第一個(gè)參數(shù)obj一般是指代理類(lèi),method是被代理的方法,如上例中的trading(),
args為該方法的參數(shù)數(shù)組。這個(gè)抽象方法在代理類(lèi)(代理處理類(lèi))中動(dòng)態(tài)實(shí)現(xiàn)。 Proxy:該類(lèi)即為動(dòng)態(tài)代理類(lèi)。 Static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h): 返回代理類(lèi)的一個(gè)實(shí)例,返回后的代理類(lèi)可以當(dāng)作被代理類(lèi)使用 (可使用被代理類(lèi)的在接口中聲明過(guò)的方法)。
總結(jié): 動(dòng)態(tài)代理類(lèi)是一個(gè)實(shí)現(xiàn)在創(chuàng)建類(lèi)并運(yùn)行時(shí)指定接口列表的類(lèi), 1.代理接口是代理類(lèi)所實(shí)現(xiàn)的一個(gè)接口。 2.代理實(shí)例是代理類(lèi)的一個(gè)實(shí)例。 3.每一個(gè)代理實(shí)例都有一個(gè)關(guān)聯(lián)的調(diào)用處理程序?qū)ο螅梢詫?shí)現(xiàn)接口InvocationHandler。 4.通過(guò)調(diào)用代理處理器中的Invoke方法實(shí)現(xiàn)代理,并傳遞代理實(shí)例,識(shí)別調(diào)用方法以及方法上的參數(shù)數(shù)組。 5.調(diào)用對(duì)象加載器以及代理處理器中的方法,并以代理實(shí)例為結(jié)果返回。
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注