java高级编程-使用反射强制给private字段赋值

今天项目中遇到了一个问题,要调用一个类,并获取这个类的属性进行赋值然后将这个类传递到方法中做为参数。

实际操作时才发现,这个类中的字段属性是私有的,不能进行赋值!没有提供公有的方法。而这个类又是打包成jar给我的,我还不能更改它的代码,以至于想手动给它写个set方法都是问题。后来想到用反射可以解决这个问题,于是试了一下,果然!

反射看来根本不区分是否是private的,调用本身的私有方法是可以的,但是调用父类的私有方法则不行,纠其原因很有可能是因为getDeclaredMethod方法和getMethod方法并不会查找父类的私有方法,自己写递归可以解决,不过利用反射来做的话性能不会太好。

我们来看下面这个代码。

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加载的应用程序不拥有此权限。例如:当我们以这种方式来执行上述程序时将会抛出异常 
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 ReflectPermission
   
   
    
    extends BasicPermission
   
   
反射操作的 Permission 类。ReflectPermission 是一种指定权限,没有动作。当前定义的唯一名称是suppressAccessChecks,它允许取消由反射对象在其使用点上执行的标准 Java 语言访问检查 - 对于 public、default(包)访问、protected、private 成员。

下表提供了允许权限的简要说明,并讨论了授予代码权限的风险。
权限目标名称权限允许的内容允许此权限的风险
suppressAccessChecks
能够访问类中的字段和调用方法。注意,这不仅包括 public、而且还包括 protected 和 private 字段和方法。
存在的风险是,通常不可用的信息(也许是保密信息)和方法可能会接受恶意代码访问。


这里就一点了然了。fields.setAccessible(true);的实际作用就是使权限可以访问public,protected,private的字段!

是不是很爽呢。当然这种方法破坏了JAVA原有的权限体系。所以不到万不得已,还是少用,反射的效率毕竟不是那么高滴。

好,知道了这个我们再来写一个通用的万能方法,只是传递相应的类,字段名称和值,我们在方法内部将其反射并进行实例化。然后进行相应字段的赋值。由于我只用到了字段。你可以加上其它的东东。嗯。这个好玩。

package unit.sms;

public class Smss {
	private String destID;
	private String content;
	private String mobile;
	public String getDestID() {
		return destID;
	}
	public String getContent() {
		return content;
	}
	public String getMobile() {
		return mobile;
	}
	
}



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(Smss.class, "destID,01201101", "mobile,15810022404", "content,测试数据。");
                Smss sms = (Smss) object;
                System.out.println("短信内容:" + sms.getContent());
                System.out.println("手机号码:" + sms.getMobile());
                System.out.println("尾号:" + sms.getDestID());
	}
}

控制台输出:

短信内容:测试数据。
手机号码:15810022404
尾号:01201101


fields.setAccessible(true);的使用可能大家都会,但我们要做的是,知其然,知其所以然。

看JDK的源码,无疑是学习和解决此方法的最佳途径。


over~~~



  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值