通常状况下,咱们并不能对类的私有字段进行操做,但有的时候咱们又必须有能力去处理这些字段,这时候,咱们就须要调用AccessibleObject上的setAccessible()方法来容许这种访问,而因为反射类中的Field,Method和Constructor继承自AccessibleObject,所以,经过在这些类上调用setAccessible()方法,咱们能够实现对这些字段的操做。今天项目中遇到了一个问题,要调用一个类,并获取这个类的属性进行赋值而后将这个类传递到方法中作为参数。
实际操做时才发现,这个类中的字段属性是私有的,不能进行赋值!没有提供公有的方法。而这个类又是打包成jar给个人,我还不能更改它的代码,以致于想手动给它写个set方法都是问题。后来想到用反射能够解决这个问题,因而试了一下,果真!java
反射看来根本不区分是不是private的,调用自己的私有方法是能够的,可是调用父类的私有方法则不行,纠其缘由颇有多是由于getDeclaredMethod方法和getMethod方法并不会查找父类的私有方法,本身写递归能够解决,不过利用反射来作的话性能不会太好。
咱们来看下面这个代码。
shell
Field[] fields = obj.getDeclaredFields(); for (int i = 0; i < fields.length; i++) { fields[i].setAccessible(true); for (int j = 0; j < args.length; j++) { String str = args[j]; String strs[] = str.split(","); if (strs[0].equals(fields[i].getName())) { fields[i].set(object, strs[1]); break; } } }
fields[i].setAccessible(true);
这句话是关键。看它的表面英文意思是设置可进入可访问为:true。编程意思你们猜测也应该知道了。
经过查看JDK的源码:
编程
public void setAccessible(boolean flag) throws SecurityException { SecurityManager sm = System.getSecurityManager(); if (sm != null) sm.checkPermission(ACCESS_PERMISSION); setAccessible0(this, flag); }
咱们能够看到它是经过SecurityManager来管理权限的,咱们能够启用java.security.manager来判断程序是否具备调用setAccessible()的权限。默认状况下,内核API和扩展目录的代码具备该权限,而类路径或经过URLClassLoader加载的应用程序不拥有此权限。
例如:当咱们以这种方式来执行上述程序时将会抛出异常
api
java.security.AccessControlException: access denied
通常状况下,咱们并不能对类的私有字段进行操做,但有的时候咱们又必须有能力去处理这些字段,这时候,咱们就须要调用AccessibleObject上的setAccessible()方法来容许这种访问,而因为反射类中的Field,Method和Constructor继承自AccessibleObject,所以,经过在这些类上调用setAccessible()方法,咱们能够实现对这些字段的操做。
咱们来看看这个ACCESS_PERMISSION里面究竟怎么处理的:
工具
static final private java.security.Permission ACCESS_PERMISSION = new ReflectPermission("suppressAccessChecks");
查找JDK帮助文档能够看到详细解释:
性能
public final class ReflectPermissionextends BasicPermission
反射操做的 Permission 类。ReflectPermission 是一种指定权限,没有动做。当前定义的惟一名称是suppressAccessChecks,它容许取消由反射对象在其使用点上执行的标准 Java 语言访问检查 - 对于 public、default(包)访问、protected、private 成员。
下表提供了容许权限的简要说明,并讨论了授予代码权限的风险。学习
权限目标名称 |
权限容许的内容 | 容许此权限的风险 |
suppressAccessChecks | 可以访问类中的字段和调用方法。注意,这不只包括 public、并且还包括 protected 和 private 字段和方法。 | 存在的风险是,一般不可用的信息(也许是保密信息)和方法可能会接受恶意代码访问。 |
这里就一点了然了。fields.setAccessible(true);的实际做用就是使权限能够访问public,protected,private的字段!
是否是很爽呢。固然这种方法破坏了JAVA原有的权限体系。因此不到万不得已,仍是少用,反射的效率毕竟不是那么高滴。
好,知道了这个咱们再来写一个通用的万能方法,只是传递相应的类,字段名称和值,咱们在方法内部将其反射并进行实例化。而后进行相应字段的赋值。因为我只用到了字段。你能够加上其它的东东。嗯。这个好玩。
测试
package com.sinoglobal.utils; import java.lang.reflect.Field; import com.jasson.mas.api.smsapi.Sms; /** * 反射的通用工具类 * * @author lz * */ public class ReflectionUtils { /** * 用于对类的字段赋值,无视private,project修饰符,无视set/get方法 * @param c 要反射的类 * @param args 类的字段名和值 每一个字段名和值用英文逗号隔开 * @return */ @SuppressWarnings("unchecked") public static Object getInstance(Class c, String... args) { try { Object object = Class.forName(c.getName()).newInstance(); Class<?> obj = object.getClass(); Field[] fields = obj.getDeclaredFields(); for (int i = 0; i < fields.length; i++) { fields[i].setAccessible(true); for (int j = 0; j < args.length; j++) { String str = args[j]; String strs[] = str.split(","); if (strs[0].equals(fields[i].getName())) { fields[i].set(object, strs[1]); break; } } } return object; } catch (IllegalAccessException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } return null; } public static void main(String[] args) { Object object=getInstance(Sms.class,"destID,01201101","mobile,15810022404","content,测试数据。"); Sms sms=(Sms)object; System.out.println("短信内容:"+sms.content); System.out.println("手机号码:"+sms.mobile); System.out.println("尾号:"+sms.destID); } }
控制台输出:
this
短信内容:测试数据。 手机号码:15810022404 尾号:01201101
fields.setAccessible(true);的使用可能你们都会,但咱们要作的是,知其然,知其因此然。
看JDK的源码,无疑是学习和解决此方法的最佳途径。
spa