【问题标题】:Java reflection - impact of setAccessible(true)Java 反射 - setAccessible(true) 的影响
【发布时间】:2012-05-25 05:07:00
【问题描述】:

我正在使用一些注释来动态设置类中字段的值。由于无论它是公共的、受保护的还是私有的,我都想执行此操作,因此每次在调用 set() 方法之前,我都会在 Field 对象上调用 setAccessible(true)。我的问题是setAccessible() 电话对球场本身有什么样的影响?

更具体地说,假设它是一个私有字段,并且这组代码调用setAccessible(true)。如果代码中的其他地方是通过反射检索相同的字段,该字段是否已经可以访问?还是 getDeclaredFields()getDeclaredField() 方法每次都返回一个 Field 对象的新实例?

我想另一种表述问题的方式是,如果我打电话给setAccessible(true),完成后将其恢复为原始值有多重要?

【问题讨论】:

    标签: java reflection


    【解决方案1】:

    使用setAccessible() 可以更改AccessibleObject 的行为,即Field 实例,但不会更改类的实际字段。这是documentation(摘录):

    true 值表示反射对象在使用时应禁止检查 Java 语言访问控制

    还有一个可运行的例子:

    public class FieldAccessible {
        public static class MyClass {
            private String theField;
        }
    
        public static void main(String[] args) throws Exception {
            MyClass myClass = new MyClass();
            Field field1 = myClass.getClass().getDeclaredField("theField");
            field1.setAccessible(true);
            System.out.println(field1.get(myClass)); // no exception
            Field field2 = myClass.getClass().getDeclaredField("theField");
            System.out.println(field2.get(myClass)); // IllegalAccessException
        }
    
    }
    

    【讨论】:

    • @PhilipRego 您需要自己编写导入声明。我希望你知道该怎么做。
    • 发现问题。你必须抛出或处理 NoSuchFieldException 或 parent。
    • 是的,这只是示例代码。我的意思是,throws Exception 也处理 NoSuchFieldException,但您可能希望以更精细的方式处理它。
    • 我在以下方面遇到异常:字段 field1 = myClass.getClass().getDeclaredField("theField");所以它甚至不编译,即 setAccessible 甚至都不重要?
    【解决方案2】:

    getDeclaredField 方法每次都必须返回一个新对象,正是因为该对象具有可变的accessible 标志。所以不需要重新设置标志。您可以在this blog post 中找到完整的详细信息。

    【讨论】:

      【解决方案3】:

      正如其他海报所指出的,setAccessible 仅适用于您的java.lang.reflect.Field 实例,因此不需要将可访问性设置回其原始状态。

      不过……

      如果您希望对field.setAccessible(true) 的调用持续存在,您需要使用java.lang.Classjava.lang.reflect.Field 中的底层方法。面向公众的方法会向您发送Field 实例的副本,因此在您每次执行class.getField(name) 之类的操作后“忘记”

      import java.lang.reflect.*;
      import sun.reflect.FieldAccessor;
      
      public class Reflect {
          private static Method privateGetDeclaredFields;
          private static Method getFieldAccessor;
      
          public static Field[] fields(Class<?> clazz) throws Exception {
              return (Field[]) privateGetDeclaredFields.invoke(clazz, false);
          }
      
          public static <T> T get(Object instance, Field field) throws Exception {
              return ((FieldAccessor) getFieldAccessor.invoke(field, instance)).get(instance);
          }
      
          public static void set(Object instance, Field field, Object value) throws Exception {
              ((FieldAccessor) getFieldAccessor.invoke(field, instance)).set(instance, value);
          }
      
          static {
              try {
                  // These are used to access the direct Field instances instead of the copies you normally get through #getDeclaredFields.
                  privateGetDeclaredFields = Class.class.getDeclaredMethod("privateGetDeclaredFields", boolean.class);
                  privateGetDeclaredFields.setAccessible(true);
                  getFieldAccessor = Field.class.getDeclaredMethod("getFieldAccessor", Object.class);
                  getFieldAccessor.setAccessible(true);
              } catch (Exception e) {
                  // Should only occur if the internals change.
                  e.printStackTrace();
              }
          }
      }
      

      更新:此实现适用于 Java 8,未来版本会更改破坏此功能的后端。如果您真的希望继续此策略,同样的概念仍然适用。

      【讨论】:

        【解决方案4】:
        import java.lang.reflect.Field;
        import java.lang.reflect.Method;
        
        public class PrivateVariableAcc {
        
            public static void main(String[] args) throws Exception {
                PrivateVarTest myClass = new PrivateVarTest();
                Field field1 = myClass.getClass().getDeclaredField("a");
                field1.setAccessible(true);
                System.out.println("This is access the private field-"
                    + field1.get(myClass));
                Method mm = myClass.getClass().getDeclaredMethod("getA");
                mm.setAccessible(true);
                System.out.println("This is calling the private method-"
                    + mm.invoke(myClass, null));
            }
        
        }
        

        【讨论】:

          猜你喜欢
          • 2020-05-31
          • 2020-04-12
          • 2014-06-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多