黑马程序员技术交流社区

标题: 关于通过reflect暴力访问私有成员变量的问题... [打印本页]

作者: 折纸时代    时间: 2013-3-7 14:58
标题: 关于通过reflect暴力访问私有成员变量的问题...
本帖最后由 司懿卓 于 2013-3-7 16:02 编辑

代码:  通过暴力访问得到成员变量数组,在循环中不用设置可见访问,就可以直接操作.

  1. import java.lang.reflect.*;
  2. class ReplaceDemo
  3. {
  4.         private String name = "abcd";
  5.         private int num = 163;
  6.         private String str = "bedg";
  7.         private ReplaceDemo(){}
  8.         public static void main(String[] args) throws Exception
  9.         {
  10.                 ReplaceDemo rd = new ReplaceDemo();
  11.                 System.out.println("替换前: " + rd);
  12.                 rd.replaceChar("b", "a");
  13.                 System.out.println("替换后: " + rd);

  14.         }
  15.         public void replaceChar(String oldChar, String newChar) throws Exception
  16.         {
  17.                 Field[] fields = this.getClass().getDeclaredFields(); //暴力访问
  18.                 for(Field field : fields){
  19.                         if(field.getType() == String.class){
  20.                                 String oldVal = (String)field.get(this);
  21.                                 String newVal = oldVal.replace(oldChar, newChar);
  22.                                 field.set(this, newVal);  //更新成员变量值
  23.                         }
  24.                 }
  25.         }
  26.         public String toString()
  27.         {
  28.                 return "[name:" + name + " num:" + num + " str:"+ str + "]";
  29.         }
  30. }
复制代码
如果类成员变量和主程序(包括功能方法)在同个类里时,功能方法内部可以通过this来替代对象.
这时,通过暴力访问私有成员变量,是不用设置访问可见的. 就四 setAccessible(true); 方法


代码2:
  1. package com.itheima;
  2. import java.lang.reflect.*;
  3. public class PrivateFieldDemo {
  4.         public static void main(String[] args)throws Exception{
  5.                 Demo demo = new Demo();
  6.                 replace(demo);
  7.         }
  8.         public static  void replace(Object obj) throws Exception{
  9.                 Field[] fields = obj.getClass().getDeclaredFields();
  10.                 for(Field field : fields){
  11.                         field.setAccessible(true);  //访问可以必须设置
  12.                         if(field.getType() == String.class){
  13.                                 String oldVal = (String)field.get(obj);
  14.                                 String newVal = oldVal.replace("a", "qqq");
  15.                                 field.set(obj,newVal);
  16.                                 System.out.println((String)field.get(obj));
  17.                         }                       
  18.                 }       
  19.         }       
  20. }
  21. class Demo{  //实例类和主程序分离
  22.         private String name = "ablckddnsdfsdfsdf";
  23.         private String test = "aldkjfskfbsdkfjsdkf";
  24.         public String toString(){
  25.                 return name + ": " + test;
  26.         }
  27. }
复制代码
如果类成员变量和主程序分开两个类时,功能内部通过传入对象来操作.
这时,通过暴力访问私有变量成员,必须设置访问可见,否则无法JVM报告无法找到该成员变量.

这里面具体是为什么呢?  如果第一种情况时,把暴力访问方法修改为普通方法,是无法访问的. 应该不是内部可见,外部不可见的问题.
求真相..


作者: lzw123451    时间: 2013-3-7 15:23
public Field[] getDeclaredFields()
                         返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段。包括公共、保护、默认(包)访问和私有字段,但不包括继承的字段。返回数组中的元素没有排序,也没有任何特定的顺序。

public Field[] getFields()
                  返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段


这里可没说内部可见,只有公共字段,public Field[] getFields()才能接收, 这只关修饰符权限的问题。

作者: 折纸时代    时间: 2013-3-7 15:57
李志卫 发表于 2013-3-7 15:23
public Field[] getDeclaredFields()
                         返回 Field 对象的一个数组,这些对象反映 ...

暴力get后,是需要设置访问可见的. 就是设置是否检查权限. true的话,不检查.

但是上面的程序,暴力get后,并不需要设置访问可见,就可以访问 . 这是一个问题.  
在对象代码和主程序代码分离后, 是必须设置访问可见的,不然提示找不到该成员变量.
作者: lzw123451    时间: 2013-3-7 16:22
司懿卓 发表于 2013-3-7 15:57
暴力get后,是需要设置访问可见的. 就是设置是否检查权限. true的话,不检查.

但是上面的程序,暴力get后, ...

是啊  你上面已经说了setAccessible(true); 方法 在第一种情况暴力反射时,可以不设置,因为在同一个类,所有成员都可见,  但第二种情况分开了两个类,所以要设置setAccessible(true); 方法

你问的是 如果第一种情况时,把暴力访问方法修改为普通方法,是无法访问的

我觉得就是因为,即使第一种情况在内部有权限修改任意权限成员,但是你通过反射获取(普通get)的成员如果只有获取了公共的成员,那么即使你有权限修改私有成员,但你也没有获取到私有成员。

我的意思是说,暴力反射能让你获取到私有成员,然后访问、修改
而普通反射根本就没获取到私有成员,那就根本谈不上访问了?即使你设了setAccessible(true); 也无法访问啊,因为你根本就没获取啊。

。。。。有点拗口, 我不知道你是不是要问这个问题
作者: 折纸时代    时间: 2013-3-7 20:54
本帖最后由 司懿卓 于 2013-3-7 21:09 编辑
李志卫 发表于 2013-3-7 16:22
是啊  你上面已经说了setAccessible(true); 方法 在第一种情况暴力反射时,可以不设置,因为在同一个类, ...

呵呵, 可能我说明的不太清晰. . .  
不过你也提醒了我.  
之前我在想,类内部成员可以互访,但反射是通过字节码文件获取得到的class对象,那么应该脱离内部成员的范畴了.  所以才有了以上两个程序来测试, 貌似是像是内部可以访问私有.
但是这样又出来一个问题,就是,既然内部访问不用检查权限(就是setAccessible()方法),那么是否在类内部获取反射对象可以不去考虑权限问题? 因为内部允许访问私有?
但是,通过普通的获取方法却又不能获取到私有成员, 这样感觉有点前后矛盾的样子.
或者我对获取的方法理解还不够..  
------------------------------------------------
是我的问题, 哈哈..  普通方法是获取公共成员的.  应该是根据修饰符来判断的, 非public 就直接不管了..  
谢谢啦..  




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2