文档章节

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

YOTOO
 YOTOO
发布于 2014/04/16 11:07
字数 1371
阅读 359
收藏 13

        一般情况下,我们并不能对类的私有字段进行操作,但有的时候我们又必须有能力去处理这些字段,这时候,我们就需要调用AccessibleObject上的setAccessible()方法来允许这种访问,而由于反射类中的Field,Method和Constructor继承自AccessibleObject,因此,通过在这些类上调用setAccessible()方法,我们可以实现对这些字段的操作。今天项目中遇到了一个问题,要调用一个类,并获取这个类的属性进行赋值然后将这个类传递到方法中做为参数。

        实际操作时才发现,这个类中的字段属性是私有的,不能进行赋值!没有提供公有的方法。而这个类又是打包成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 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);  
    }  
}

控制台输出:

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

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

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

本文转载自:http://blog.csdn.net/yaerfeng/article/details/7103397

YOTOO
粉丝 2
博文 18
码字总数 11119
作品 0
闵行
项目经理
私信 提问
阿里P7浅谈Java的Spring Bean

1、简单java 类,实现 Serializable 接口 package com.curtis.bean;import java.io.Serializable;@SuppressWarnings("serial")public class Person implements Serializable { private Strin......

Java互联网架构师
2018/09/03
0
0
秒懂Java动态编程(Javassist研究)

[toc] 概述 什么是动态编程?动态编程解决什么问题?Java中如何使用?什么原理?如何改进?(需要我们一起探索,由于自己也是比较菜,一般深入不到这个程度)。 什么是动态编程 动态编程是相...

MyOldTime
07/03
71
0
编程思想 之「运行时类型识别、反射」

版权声明:Follow your heart and intuition. https://blog.csdn.net/qq_35246620/article/details/79576706 温馨提示:本系列博文(含示例代码)已经同步到 GitHub,地址为「java-skills」,...

维C果糖
2018/03/16
0
0
对struts2的action接收参数的理解

只接收参数值(非对象) Action中的参数若前台只是单纯的传递值 如:Form中 后台接收的方法只要有setUserName();就可以了,因为是单纯的赋值。 后台对象接收参数 请求的格式: testAction? ...

低至一折起
08/19
7
0
优雅的JAVA工具库LOMBOK

优雅的Java工具库Lombok 最近在公司的项目中看到了对于Lombok的应用,通过@Data注解标注POJO,省略了大量的getter/setter代码,原先冗长的POJO在瘦身之后直接变得干净、清爽,程序员再也不需...

编程SHA
01/02
114
0

没有更多内容

加载失败,请刷新页面

加载更多

前端技术之:Prisma Demo服务部署过程记录

安装前提条件: 1、已经安装了docker运行环境 2、以下命令执行记录发生在MackBook环境 3、已经安装了PostgreSQL(我使用的是11版本) 4、Node开发运行环境可以正常工作 首先需要通过Node包管...

popgis
今天
5
0
数组和链表

数组 链表 技巧一:掌握链表,想轻松写出正确的链表代码,需要理解指针获引用的含义: 对指针的理解,记住下面的这句话就可以了: 将某个变量赋值给指针,实际上就是将这个变量的地址赋值给指...

code-ortaerc
今天
4
0
栈-链式(c/c++实现)

上次说“栈是在线性表演变而来的,线性表很自由,想往哪里插数据就往哪里插数据,想删哪数据就删哪数据...。但给线性表一些限制呢,就没那么自由了,把线性表的三边封起来就变成了栈,栈只能...

白客C
今天
42
0
Mybatis Plus service

/** * @author beth * @data 2019-10-20 23:34 */@RunWith(SpringRunner.class)@SpringBootTestpublic class ServiceTest { @Autowired private IUserInfoService iUserInfoS......

一个yuanbeth
今天
5
0
php7-internal 7 zval的操作

## 7.7 zval的操作 扩展中经常会用到各种类型的zval,PHP提供了很多宏用于不同类型zval的操作,尽管我们也可以自己操作zval,但这并不是一个好习惯,因为zval有很多其它用途的标识,如果自己...

冻结not
昨天
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部