一.反射进阶之动态设置类的私有域
"封装"是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注:Java 中的数组不管是几维的,都是Object类型的s
componentType: 表示新数组的组件类型的Class对象
length: 新数组的长度
代码实例:
可变数组工具类的实现:
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.调用对象加载器以及代理处理器中的方法,并以代理实例为结果返回。