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

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

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

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

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

我们来看下面这个代码。

  1. Field[] fields = obj.getDeclaredFields();  
  2.             for (int i = 0; i < fields.length; i++) {  
  3.                 fields[i].setAccessible(true);  
  4.                 for (int j = 0; j < args.length; j++) {  
  5.                     String str = args[j];  
  6.                     String strs[] = str.split(",");  
  7.                     if (strs[0].equals(fields[i].getName())) {  
  8.                         fields[i].set(object, strs[1]);  
  9.                         break;  
  10.                     }  
  11.                 }  
  12.             } 

 

  1. fields[i].setAccessible(true); 

这句话是关键。看它的表面英文意思是设置可进入可访问为:true。编程意思大家猜想也应该知道了。

通过查看JDK的源码:

  1. public void setAccessible(boolean flag) throws SecurityException {  
  2.     SecurityManager sm = System.getSecurityManager();  
  3.     if (sm != null) sm.checkPermission(ACCESS_PERMISSION);  
  4.     setAccessible0(this, flag);  
  5.     } 

我们可以看到它是通过SecurityManager来管理权限的,我们可以启用java.security.manager来判断程序是否具有调用setAccessible()的权限。默认情况下,内核API和扩展目录的代码具有该权限,而类路径或通过URLClassLoader加载的应用程序不拥有此权限。

例如:当我们以这种方式来执行上述程序时将会抛出异常

  1. java.security.AccessControlException:   access   denied  

一般情况下,我们并不能对类的私有字段进行操作,但有的时候我们又必须有能力去处理这些字段,这时候,我们就需要调用AccessibleObject上的setAccessible()方法来允许这种访问,而由于反射类中的Field,Method和Constructor继承自AccessibleObject,因此,通过在这些类上调用setAccessible()方法,我们可以实现对这些字段的操作。

我们来看看这个ACCESS_PERMISSION里面究竟怎么处理的:

  1. static final private java.security.Permission ACCESS_PERMISSION =  
  2. new ReflectPermission("suppressAccessChecks"); 

查找JDK帮助文档可以看到详细解释:

  1. public final class ReflectPermissionextends BasicPermission 

反射操作的 Permission 类。ReflectPermission 是一种指定权限,没有动作。当前定义的唯一名称是suppressAccessChecks,它允许取消由反射对象在其使用点上执行的标准 Java 语言访问检查 - 对于 public、default(包)访问、protected、private 成员。

下表提供了允许权限的简要说明,并讨论了授予代码权限的风险。

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

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

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

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

  1. package com.sinoglobal.utils;  
  2.  
  3. import java.lang.reflect.Field;  
  4.  
  5. import com.jasson.mas.api.smsapi.Sms;  
  6.  
  7. /**  
  8.  * 反射的通用工具类  
  9.  *   
  10.  * @author lz  
  11.  *   
  12.  */ 
  13. public class ReflectionUtils {  
  14.       
  15.     /**  
  16.      * 用于对类的字段赋值,无视private,project修饰符,无视set/get方法  
  17.      * @param c 要反射的类  
  18.      * @param args 类的字段名和值 每个字段名和值用英文逗号隔开  
  19.      * @return  
  20.      */ 
  21.     @SuppressWarnings("unchecked")  
  22.     public static Object getInstance(Class c, String... args) {  
  23.         try {  
  24.             Object object = Class.forName(c.getName()).newInstance();  
  25.             Class<?> obj = object.getClass();  
  26.             Field[] fields = obj.getDeclaredFields();  
  27.             for (int i = 0; i < fields.length; i++) {  
  28.                 fields[i].setAccessible(true);  
  29.                 for (int j = 0; j < args.length; j++) {  
  30.                     String str = args[j];  
  31.                     String strs[] = str.split(",");  
  32.                     if (strs[0].equals(fields[i].getName())) {  
  33.                         fields[i].set(object, strs[1]);  
  34.                         break;  
  35.                     }  
  36.                 }  
  37.             }  
  38.             return object;  
  39.         } catch (IllegalAccessException e) {  
  40.             e.printStackTrace();  
  41.         } catch (ClassNotFoundException e) {  
  42.             e.printStackTrace();  
  43.         } catch (InstantiationException e) {  
  44.             e.printStackTrace();  
  45.         }  
  46.         return null;  
  47.     }  
  48.     public static void main(String[] args) {  
  49.         Object object=getInstance(Sms.class,"destID,01201101","mobile,15810022404","content,测试数据。");  
  50.         Sms sms=(Sms)object;  
  51.         System.out.println("短信内容:"+sms.content);  
  52.         System.out.println("手机号码:"+sms.mobile);  
  53.         System.out.println("尾号:"+sms.destID);  
  54.     }  

控制台输出:

短信内容:测试数据。

手机号码:15810022404

尾号:01201101

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

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

原文链接:http://blog.csdn.net/yaerfeng/article/details/7103397

【编辑推荐】

  1. 使用Java 2D绘制黑白太极图案
  2. Java并发编程:守护线程
  3. 深入Java虚拟机之内存优化
  4. 深入Java虚拟机之虚拟机体系结构
  5. 从Java的角度理解Ext的extend
责任编辑:林师授 来源: yaerfeng的博客
相关推荐

2011-08-15 17:15:45

CASE语句Oracle给指定字段赋值

2009-08-31 14:07:57

C# private字C# public属性

2022-12-14 08:42:46

IDEAIDEA插件

2017-05-26 10:15:39

Java高级特性反射

2021-11-28 18:23:53

Java特性序列化

2011-08-29 13:10:59

SQL Server 给新加字段赋值

2024-07-30 14:46:55

2011-10-08 11:05:04

GPUMATLAB

2023-06-27 08:37:35

Java反射动态代理机制

2024-03-04 18:49:59

反射C#开发

2024-09-11 08:00:00

Java优于反射开发

2024-05-24 10:51:51

框架Java

2018-06-07 09:29:34

数据库MySQL慢SQL

2021-03-22 08:45:30

异步编程Java

2019-11-27 10:05:00

LombokJava编程

2015-08-07 09:33:24

RuntimeModel

2021-10-30 18:38:49

Java c++反射

2023-11-06 13:32:38

Go编程

2010-11-12 10:46:16

SQL Server变

2017-01-24 19:57:13

Linux命令Shell
点赞
收藏

51CTO技术栈公众号