一.反射進階之動態設置類的私有域
"封裝"是Java的三大特性之一,為了能更好保證其封裝性,我們往往需要將域設置成私有的,
然后通過提供相對應的set和get方法來操作這個域。但是我們仍然可以用java的反射機制來
修改類的私有域,由於修改類的私有域會破壞Java"封裝"的特性,故請慎重操作。
主要技術:
Field類提供有關類或接口的單個字段的信息,以及對它的動態訪問權限。
訪問的字段可能是一個類(靜態)字段或實例字段。
常用方法:
set(Object obj,Object value)-----: 將指定對象變量上此Field對象表示的字段設置為指定的新值
setBoolean(Object obj,boolean z)-: 將字段使得設置為指定對象上的一個boolean值
setDouble(Object obj,double d)---: 將字段的值設置為指定對象上的一個double值
setInt(Object obj,int i)---------: 將字段的值設置為指定對象上的一個int值
setAccessible(boolean flag)------: 將此對象的accessible標志設置為指定的布爾值
注:對於私有域,外部類調用的時候一定要使用setAccessible()方法將其可見性設置為true才能設置新值,
否則將會拋出異。
--支持知識共享,轉載請標注地址"http://www.cnblogs.com/XHJT/p/3922160.html "——和佑博客園,謝謝~~--
實例代碼:
package com.xhj.reflection_excetion; import java.lang.reflect.Field; /** * 動態修改類的私有域 * * @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; } /** * 根據獲得的性別的真假,獲取其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, "北京路華西小區3單元446", true); Class<?> clazz = user.getClass(); System.out.println("通過反射取得的對象全稱為:" + clazz.getName()); try { // 獲取要修改的類的字段名稱 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(); } } }
結果為:
注:任何類型的域,都可以通過set(Object obj, Object value) 方法將指定對象變量上此 Field 對象表示的字段設置為指定的新值。
但是Field本身也提供了其他類型相對應的set方法,如 setBoolean(Object obj, boolean z),setDouble(Object obj, double d)等。另外,通過Field也可以設置public.protected域,但一般情況下很少這么設置,尤其是public域。在這里特別要注意的是:一定
要明確修改的含義,不要輕易的通過反射來修改類的私有域,因為這破壞了java面向對象"封裝"的特性。
二、反射進階之動態調用類的方法
我們知道Java是一種面向對象的語言,對他而言,一切都是對象,因此要調用類的方法,只能通過建立類的對象來調用。當然如果是
靜態的方法,那就可以直接通過類本身來調用,而不需要建立類的對象。那么還有沒有其他可以調用類方法的方式呢??
在Java的反射的機制中,提供了比較另類的調用方式,既可以根據需要指定要調用的方法,而不必在編程時確定。調用的方法不僅權限於public
的,還可以是private的。
Method類提供類或接口上單獨某個方法(以及如何訪問該方法)的信息,所反映的方法可能是類方法或實例方法(包括抽象方法)。它允許在匹配
要調用的實參與底層方法的形參時進行擴展轉換,但是如果要進行收縮轉換,則會拋出"非法參數異常"--IllegalArgumentExcetion。使用invoke()
可以實現動態調用方法:
public Object invoke(Object obj,Object...args)throws IllegalArgumentException,IllegalAccessException,InvocationTargetExcetion
obj--要調用的方法的類對象
args--方法調用的參數
注:對於私有域,外部類調用的時候一定要使用setAccessible,並且設置為true。
實例代碼:
package com.xhj.reflection_excetion; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /** * 實現動態調用類的方法 * * @author XIEHEJUN * */ public class DynamicCallMethod { public static void main(String[] args) { try { // 運行時動態調用Math的abs()靜態方法 System.out.println("運行時動態調用Math的abs()靜態方法"); Method meth = Math.class.getDeclaredMethod("abs", Integer.TYPE); Integer a = (Integer) meth.invoke(null, new Integer(-1)); System.out.println("-1的絕對值為:" + a); // 運行時動態調用String的非靜態方法contains() System.out.println("運行時動態調用String的非靜態方法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(); } } }
結果為:
三、反射進階之動態實例化類
在Java中,要實例化一個類即創建一個類的對象,需要通過構造方法來實現。但是在Java中還可以使用另外一種方式來實現,
那就是通過反射來實例化一個類。
Constructor是Java中提供類的單個構造方法的信息以及訪問權限的封裝類。
它允許在將實參與帶有底層構造方法的形參的newInstance()匹配時進行擴展轉換,
但是如果發生收縮轉換,則拋出IllegalArgumentExcetion。newInstance()方法可以
使用指定的參數來創建對象:
public T newInstance(Object...initargs)throws InstantiationException,IllegalAccessException,
IllegalArgumentException,InvocationTargetException
initargs: 將作為變量傳遞給構造方法調用的對象數組。
注:對於私有域,外部類調用的時候一定要使用setAccessible,並且設置為true。
代碼實例為:
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; /** * 動態實例化類 * * @author XIEHEJUN * */ public class DynamiCreateClass { public static void main(String[] args) { // TODO Auto-generated method stub try { // 動態建立一個String對象, Constructor<?> construtor = String.class .getConstructor(String.class); String str = (String) construtor.newInstance("000123"); System.out.println(Integer.parseInt(str)); // 動態建立一個txt文件,並將上面初始化后的String值寫入文件的當中 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 += "---文件創建成功"; System.out.println(str);
OutputStream os = new FileOutputStream(url); os.write(str.getBytes()); os.close(); } else { System.out.println("創建文件失敗"); } } 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(); } } }
結果為:
控制台輸出
生成的文件:
注:在java中有兩種不用new就可以建立類對象的方法,即Class.newInstance()和Constructor.newInstance();
區別在於:
前者只能調用無參構造方法,而后者卻能調用有參構造方法;
且前者需要被調用的構造方法可見,后者則在特定情況下運行調用不可見的構造方法
四、反射進階之自定義可變數組工具類
在Java中,要創建可變數組可通過ArraryList類來實現。除此之外,我們也可以用自定義的方法來實現可變數組。
這里,我們將使用Java的反射機制實現一個工具類,通過這個工具類,我們就能實現可變數組的創建。
主要技術:
Array類提供了動態創建和訪問Java數組的方法。它允許在執行get,set操作期間進行擴展轉換,但若發生收縮
轉換將拋出IllegalArgumentExcetion。為了創建新的數組對象,需要使用newInstance()方法,它可以根據指定
的元素類型和長度創建新的數組:
public static Object newInstance(Class<?> componentType,int length)throws NegativeArraySizeException
componentType: 表示新數組的組件類型的Class對象
length: 新數組的長度注:Java 中的數組不管是幾維的,都是Object類型的s
代碼實例:
可變數組工具類的實現:
package com.xhj.reflection_excetion; import java.lang.reflect.Array; /** * 用反射機制實現可變數組工具類 * * @author XIEHEJUN * */ public class VariableArrayUtil { /** * 增加的數組長度值 */ private int addLength; /** * 需要增加長度的原數組 */ private Object array; public int getaddLength() { return addLength; } public Object getArray() { return array; } /** * 可變數組初始化 * @param addLength 需要增加的數組的長度 * @param array 需要增加長度的原數組 */ public VariableArrayUtil(int addLength, Object array) { super(); this.addLength = addLength; this.array = array; } /** * 可變數組的實現 * * @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; } }
測試類:
package com.xhj.test; import com.xhj.reflection_excetion.VariableArrayUtil; /** * 可變數組工具類的測試類 * * @author XIEHEJUN * */ public class Test { public static void main(String[] args) { int[] a = new int[10]; System.out.println("原數組為:"); for (int i = 0; i < 10; i++) { a[i] = i; System.out.print(" " + a[i]); } System.out.println("\n數組長度為:" + a.length); VariableArrayUtil util = new VariableArrayUtil(5, (Object) a); int[] b = (int[]) util.newArrary(); System.out .println("==================================================\n" + "更改后的數組長度為:" + b.length); for (int i = 10; i < 15; i++) { b[i] = i; } System.out.println("更改后的數組為:"); for (int i : b) { System.out.print(" " + i); } } }
結果為:
五、反射進階之重寫toString方法
為了方便輸出對象,在Object中定義了toString的方法,其默認值由類名和哈希碼組成。但是很多時候,為了能更好的滿足我們的需求,
我們都是需要重寫這個方法的。下面我們將利用Java的反射機制,重寫這個方法,並輸出類的相關信息。
代碼實例:
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; /** * 利用反射重寫Object的toString方法 * * @author XIEHEJUN * */ public class RewriteToString { public String toString(Object obj) { Class<?> clazz = obj.getClass(); // 建立一個容器用來存儲類的信息 StringBuilder strBuilder = new StringBuilder(); strBuilder.append("以下是類的信息:"); // 類名 String className = clazz.getSimpleName(); // 包名 Package packageName = clazz.getPackage(); // 公共構造方法 Constructor<?>[] constructors = clazz.getConstructors(); // 公共域 Field[] fields = clazz.getFields(); // 接口 Type[] interfaces = clazz.getInterfaces(); // 公共方法 Method[] methods = clazz.getMethods(); strBuilder.append("\n此類的簡單名稱為--" + className); strBuilder.append("\n此類的包名為--" + packageName); strBuilder.append("\n此類的公共構造方法有:"); if (constructors.length > 0) { for (Constructor<?> constructor : constructors) { strBuilder.append("\n\t" + constructor); } } else { strBuilder.append("空"); } strBuilder.append("\n此類的公共域有:"); if (fields.length > 0) { for (Field field : fields) { strBuilder.append("\n\t" + field); } } else { strBuilder.append("空"); } strBuilder.append("\n此類的接口有:"); if (fields.length > 0) { for (Type type : interfaces) { strBuilder.append("\n\t" + type); } } else { strBuilder.append("空"); } strBuilder.append("\n此類的公共方法有:"); 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())); } }
結果為:
注:在實際開發當中,一般而言都是需要重寫toString方法的,為了更好的開發,可使用Commons Lang 組件提供的工具類來重寫此方法。
六、反射進階之動態代理
代理是Java中很重要的一個特性,使用代理可以在程序運行時創建一個實現指定接口的新類。一般而言,我們只有在程序編譯時
無法確定需要使用哪些接口時才會使用代理機制。代理更多的是用在系統的開發上,它可以為工具類提供更加靈活的特性。
InvocationHandle 接口是代理實例的調用處理程序實現的接口。每個代理實例都具有一個關聯的調用處理程序。
對代理實例調用方法時,將對方法調用進行編碼並將其指派到它的調用處理程序的invoke()方法:
Object invoke(Object proxy,Method method,Object[]args)throws Throwable
proxy:代理類
method: 代理實例上要被調用的方法
args: 代理實例上方法調用的參數數組
Proxy接口提供了用於創建動態代理類和實例的靜態方法,它是所有動態代理類的父類。
獲得一個指定接口的代理類實例:
public static Object newProxyInstance(ClassLoader loader,Class<?> [] interfaces,InvocationHandle h)throws IllegalArgumentException
loader:定義代理類的類加載器
interfaces:代理類要實現的接口列表
h:指派方法調用的代理處理程序
代碼實例:
接口:
package com.xhj.reflection_excetion.dynamicProxy.bean; /** * 代理類和被代理類的共同接口 * * @author XIEHEJUN * */ public interface DoBusiness { /** * 商品交易方式 */ public void trading(); }
被代理類:
package com.xhj.reflection_excetion.dynamicProxy.service; import com.xhj.reflection_excetion.dynamicProxy.bean.DoBusiness; /** * 被代理類--廠家 * * @author XIEHEJUN * */ public class Product implements DoBusiness { @Override public void trading() { System.out.println("廠家直銷"); } }
代理處理器:
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("商家代理定點銷售"); return null; } }
測試類:
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("沒有啟用代理模式---"); p.trading(); ClassLoader loader = p.getClass().getClassLoader(); p = (DoBusiness) Proxy.newProxyInstance(loader, Product.class.getInterfaces(), new DynamicProxy()); System.out.print("啟用動態代理模式---"); p.trading(); } }
結果為:
注:
java動態代理類位於java.lang.reflect包下,一般主要涉及到以下兩個類:
Interface InvocationHandler:該接口中僅定義了一個方法Object:invoke(Object obj,Method method, Object[] args)。
在實際使用時,第一個參數obj一般是指代理類,method是被代理的方法,如上例中的trading(),args為該方法的參數數組。這個抽象方法在代理類(代理處理類)中動態實現。
Proxy:該類即為動態代理類。
Static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):
返回代理類的一個實例,返回后的代理類可以當作被代理類使用 (可使用被代理類的在接口中聲明過的方法)。
總結:
動態代理類是一個實現在創建類並運行時指定接口列表的類,
1.代理接口是代理類所實現的一個接口。
2.代理實例是代理類的一個實例。
3.每一個代理實例都有一個關聯的調用處理程序對象,它可以實現接口InvocationHandler。
4.通過調用代理處理器中的Invoke方法實現代理,並傳遞代理實例,識別調用方法以及方法上的參數數組。
5.調用對象加載器以及代理處理器中的方法,並以代理實例為結果返回。