【问题标题】:How do I access private methods and private data members via reflection?如何通过反射访问私有方法和私有数据成员?
【发布时间】:2012-07-14 01:56:48
【问题描述】:

我知道我们可以通过反射访问私有构造函数,正如他在回答我的问题时提到的@Sanjay T. SharmaDoes “instanceof Void” always return false?

但是,@duffymosaid

你可以通过反射访问私有的一切——方法、构造函数、数据成员,一切。

  1. 如何访问私有方法和私有数据成员?
  2. 是否可以通过反射访问局部变量?
  3. 有没有办法阻止任何人访问私有构造函数、方法和数据成员?

【问题讨论】:

  • 局部变量存在于栈上,而不是堆上,所以这是一个完全不同的概念。但无论如何有趣的问题。
  • 1.到处都是重复的,例如:stackoverflow.com/questions/1555658/…
  • @Thilo 那只是数据成员,方法呢?是不是一样的?
  • (您可以查看SecurityManager 以使事情正常运行。(您可以通过调试/工具接口或字节码注入访问本地字段。)

标签: java security reflection private


【解决方案1】:

示例如下:

import java.lang.reflect.Constructor;

import java.lang.reflect.Field;

import java.lang.reflect.InvocationTargetException;

import java.lang.reflect.Method;

class Test
{
    private int a = 5;   // Private data member

    private void call(int n) // Private method
    {
        System.out.println("in call()  n: " + n);
    }
}

public class Sample
{
    public static void main(String args[]) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchFieldException
    {
        Class c = Class.forName("Test");

        Object obj = c.newInstance();

        //---- Accessing a private method
        Method m=c.getDeclaredMethod("call",new Class[]{int.class});
        m.setAccessible(true);
        m.invoke(obj,7);

       //---- Accessing a private data member
       Field d = c.getDeclaredField("a");
       d.setAccessible(true);
       System.out.println(d.getInt(obj));
    }
}

【讨论】:

    【解决方案2】:

    回答你的第三个问题:

    1. 有没有办法阻止任何人访问私有构造函数、方法和数据成员?

    答案:

    是的,您可以限制访问(当有人试图访问您的私有构造函数/方法/数据时,您可以抛出异常)

    参考下面的例子:

    ******JavaSingleton Class******
    
    package server;
    
    public class JavaSingleton {
    
      private static final JavaSingleton INSTANCE = new JavaSingleton();
    
      private JavaSingleton() {
        if (INSTANCE != null) {
          throw new IllegalStateException("Inside JavaSingleton(): JavaSingleton " +
                                                            "instance already created.");
        }
        System.out.println("Inside JavaSingleton(): Singleton instance is being created.");
      }
    
      public static final JavaSingleton getInstance() {
        return INSTANCE;
      }
    }
    
    
    ***Listing 2: JavaSingleton client***
    
    import server.JavaSingleton;
    import java.lang.reflect.*;
    
    public class TestSingleton {
    
      public static void main(String[] args) throws ReflectiveOperationException {
        System.out.println("Inside main(): Getting the singleton instance using getInstance()...");
        JavaSingleton s = JavaSingleton.getInstance();
    
        System.out.println("Inside main(): Trying to use reflection to get another instance...");
        Class<JavaSingleton> clazz = JavaSingleton.class;
        Constructor<JavaSingleton> cons = clazz.getDeclaredConstructor();
        cons.setAccessible(true);
        JavaSingleton s2 = cons.newInstance();
      }
    }
    
    Output:
    
    C:\singleton>java TestSingleton
    Inside main(): Getting the singleton instance using getInstance()...
    Inside JavaSingleton(): Singleton instance is being created.
    Inside main(): Trying to use reflection to get another instance...
    Exception in thread "main" java.lang.reflect.InvocationTargetException
      at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
      at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
      at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
      at java.lang.reflect.Constructor.newInstance(Unknown Source)
      at TestSingleton.main(TestSingleton.java:13)
    Caused by: java.lang.IllegalStateException: Inside JavaSingleton(): JavaSingleton instance already created.
      at server.JavaSingleton.<init>(JavaSingleton.java:7)
      ... 5 more
    

    这个例子是一个单例类(检查构造函数),但你仍然可以为你想要阻止其他类访问的私有方法实现这个逻辑。

    在这种情况下,您还将声明一个静态实例并在私有方法中检查它的值,并在出现任何不需要的值时抛出错误。

    【讨论】:

      【解决方案3】:

      1) 如何访问私有方法和私有数据成员?

      你可以在setAccessible(true) 方法的帮助下做到这一点:

      class Dummy{
          private void foo(){
              System.out.println("hello foo()");
          }
          private int i = 10;
      }
      
      class Test{
          public static void main(String[] args) throws Exception {
              Dummy d = new Dummy();
      
              /*---  [INVOKING PRIVATE METHOD]  ---*/
              Method m = Dummy.class.getDeclaredMethod("foo");
              //m.invoke(d); // Exception java.lang.IllegalAccessException
              m.setAccessible(true);//Abracadabra
              m.invoke(d); // Now it's OK
      
              /*---  [GETING VALUE FROM PRIVATE FIELD]  ---*/
              Field f = Dummy.class.getDeclaredField("i");
              //System.out.println(f.get(d)); // Not accessible now
              f.setAccessible(true); // Abracadabra
              System.out.println(f.get(d)); // Now it's OK
      
              /*---  [SETTING VALUE OF PRIVATE FIELD]  ---*/
              Field f2 = Dummy.class.getDeclaredField("i");
              //f2.set(d,20); // Not accessible now
              f2.setAccessible(true); // Abracadabra
              f2.set(d, 20); // Now it's OK
              System.out.println(f2.get(d));
          }
      }
      

      2) 是否可以通过反射访问局部变量?

      没有。局部变量不能在创建它们的块之外访问(有人可能会说您可以将这样的变量分配给像 field = localVariable; 这样的字段,然后通过反射访问这样的字段,但是这样我们将访问 值,而不是变量)。

      3) 有什么方法可以阻止任何人访问私有构造函数、方法和数据成员?

      我认为对于constructorsmethods,您可以使用stacktrace 来检查它是否被Reflection 调用。
      对于字段,我找不到阻止通过反射访问它们的解决方案。

      [警告:这未经任何人批准。我只是受您的问题启发而写的。]

      class Dummy {
          private void safeMethod() {
              StackTraceElement[] st = new Exception().getStackTrace();
              // If a method was invoked by reflection, the stack trace would be similar
              // to something like this:
              /*
              java.lang.Exception
                  at package1.b.Dummy.safeMethod(SomeClass.java:38)
                  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
                  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
                  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
              ->    at java.lang.reflect.Method.invoke(Method.java:601)
                  at package1.b.Test.main(SomeClass.java:65)
              */
              //5th line marked by "->" is interesting one so I will try to use that info
      
              if (st.length > 5 &&
                  st[4].getClassName().equals("java.lang.reflect.Method"))
                  throw new RuntimeException("safeMethod() is accessible only by Dummy object");
      
              // Now normal code of method
              System.out.println("code of safe method");
          }
      
          // I will check if it is possible to normally use that method inside this class
          public void trySafeMethod(){
              safeMethod();
          }
      
          Dummy() {
              safeMethod();
          }
      }
      
      class Dummy1 extends Dummy {}
      
      class Test {
          public static void main(String[] args) throws Exception {
              Dummy1 d1 = new Dummy1(); // safeMethod can be invoked inside a superclass constructor
              d1.trySafeMethod(); // safeMethod can be invoked inside other Dummy class methods
              System.out.println("-------------------");
      
              // Let's check if it is possible to invoke it via reflection
              Method m2 = Dummy.class.getDeclaredMethod("safeMethod");
              // m.invoke(d);//exception java.lang.IllegalAccessException
              m2.setAccessible(true);
              m2.invoke(d1);
          }
      }
      

      Test 主方法的输出

      code of safe method
      code of safe method
      -------------------
      Exception in thread "main" java.lang.reflect.InvocationTargetException
          at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
          at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
          at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
          at java.lang.reflect.Method.invoke(Method.java:601)
          at package1.b.Test.main(MyClass2.java:87)
      Caused by: java.lang.RuntimeException: method safeMethod() is accessible only by Dummy object
          at package1.b.Dummy.safeMethod(MyClass2.java:54)
          ... 5 more
      

      【讨论】:

        【解决方案4】:
         Area s=(Area)c.newInstance();  
         s.setRadius(10);
         System.out.println("Area: "+s.calculateArea(4));
        
         Method m[]=c.getDeclaredMethods(); 
          Constructor c1[]=c.getConstructors();  
        
         for(int i=0;i<m.length;i++)
             System.out.println(""+m[i]);
        
         for(int i=0;i<c1.length;i++)
             System.out.println(""+c1[i]);
        

        【讨论】:

          【解决方案5】:

          要访问 私有字段,您需要调用 Class.getDeclaredField(String name)enter code here 方法。 检查这个简单的代码:

          public class PrivateObject {
          
            private String privateString = null;
          
            public PrivateObject(String privateString) {
              this.privateString = privateString;
            }
          }
          
          PrivateObject privateObject = new PrivateObject("The Private Value");
          
          Field privateStringField = PrivateObject.class.
                      getDeclaredField("privateString");
          
          privateStringField.setAccessible(true);
          
          String fieldValue = (String) privateStringField.get(privateObject);
          System.out.println("fieldValue = " + fieldValue
          

          要访问私有方法,您需要调用 Class.getDeclaredMethod(String name, Class[] parameterTypes) 或 Class.getDeclaredMethods() 方法。

          检查这个简单的代码:

          public class PrivateObject {
          
            private String privateString = null;
          
            public PrivateObject(String privateString) {
              this.privateString = privateString;
            }
          
            private String getPrivateString(){
              return this.privateString;
            }
          }
          PrivateObject privateObject = new PrivateObject("The Private Value");
          
          Method privateStringMethod = PrivateObject.class.
                  getDeclaredMethod("getPrivateString", null);
          
          privateStringMethod.setAccessible(true);
          
          String returnValue = (String)
                  privateStringMethod.invoke(privateObject, null);
          
          System.out.println("returnValue = " + returnValue);
          

          阅读详情 http://tutorials.jenkov.com/java-reflection/private-fields-and-methods.html

          【讨论】:

            【解决方案6】:
            1. 使用您链接到的答案中显示的方法:setAccessible(true),它是 Field、Constructor 和 Method 的超类的方法。
            2. 没有。
            3. 不可以,除非代码在您控制的 JVM 中运行,您可以在其中安装安全管理器。但是,如果您给某人一个 jar 文件,他使用该 jar 文件中的类,他将能够访问所有内容。

            【讨论】:

            • +1 所以这是Java的缺点之一?您无法保护您的 classes 代码。
            • 我认为这不是 Java 特有的。您无法保护“您的”代码免受运行它的用户的影响。 DRM 实施也与这一事实作斗争。远程执行 (SaaS) 可能是唯一的“解决方案”。无论哪种方式,隐藏在 OOP 中的信息都不是一种保密的安全功能,而是一种方便程序员不必看到并意外弄乱实现内部的便利。
            猜你喜欢
            • 2010-10-15
            • 1970-01-01
            • 1970-01-01
            • 2015-11-15
            • 1970-01-01
            • 1970-01-01
            • 2014-04-22
            • 2012-12-20
            • 2016-06-06
            相关资源
            最近更新 更多