有什么方法可以调用私有方法吗?


146

我有一个使用XML和反射将Objects 返回到另一个类的类。

通常,这些对象是外部对象的子字段,但是有时我想即时生成它。我已经尝试过类似的方法,但无济于事。我相信这是因为Java不允许您访问private进行反射的方法。

Element node = outerNode.item(0);
String methodName = node.getAttribute("method");
String objectName = node.getAttribute("object");

if ("SomeObject".equals(objectName))
    object = someObject;
else
    object = this;

method = object.getClass().getMethod(methodName, (Class[]) null);

如果提供的方法正确private,则失败NoSuchMethodException。我可以通过制作方法来解决它public,或者制作另一个类来派生它。

长话短说,我只是想知道是否存在private一种通过反射访问方法的方法。

Answers:


303

您可以使用反射调用私有方法。修改已发布代码的最后一位:

Method method = object.getClass().getDeclaredMethod(methodName);
method.setAccessible(true);
Object r = method.invoke(object);

有几个警告。首先,getDeclaredMethod将仅查找在current中声明的方法Class,而不继承自超类型。因此,如有必要,遍历具体的类层次结构。其次,SecurityManager可以防止使用该setAccessible方法。因此,它可能需要以PrivilegedAction(使用AccessControllerSubject)运行。


2
当我过去这样做时,在调用方法后也调用了method.setAccessible(false),但是我不知道这是否必要。
shsteimer

16
不,当您设置可访问性时,它仅适用于该实例。只要您不让该特定的Method对象从控件中逸出,它就是安全的。
埃里克森

6
那么,如果可以从类外部调用私有方法,那又有什么意义呢?
彼得·阿杰泰

2
还要确保您调用getDeclaredMethod()而不是仅getMethod()-这不适用于私有方法。
Ercksen

1
@PeterAjtai抱歉,您的回复很晚,但是您应该这样想:如今,大多数人都锁着门,即使他们知道锁可能会被轻易破坏或完全绕开。为什么?因为它有助于使大多数诚实的人保持诚实。您可以想到private访问扮演着类似的角色。
erickson


25

如果该方法接受非原始数据类型,则可以使用以下方法来调用任何类的私有方法:

public static Object genericInvokeMethod(Object obj, String methodName,
            Object... params) {
        int paramCount = params.length;
        Method method;
        Object requiredObj = null;
        Class<?>[] classArray = new Class<?>[paramCount];
        for (int i = 0; i < paramCount; i++) {
            classArray[i] = params[i].getClass();
        }
        try {
            method = obj.getClass().getDeclaredMethod(methodName, classArray);
            method.setAccessible(true);
            requiredObj = method.invoke(obj, params);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

        return requiredObj;
    }

接受的参数是obj,methodName和参数。例如

public class Test {
private String concatString(String a, String b) {
    return (a+b);
}
}

方法concatString可以作为

Test t = new Test();
    String str = (String) genericInvokeMethod(t, "concatString", "Hello", "Mr.x");

7
为什么需要paramCount?您不能只使用params.length吗?
2014年


3

让我通过反射为执行保护的方法提供完整的代码。它支持任何类型的参数,包括泛型,自动装箱的参数和空值

@SuppressWarnings("unchecked")
public static <T> T executeSuperMethod(Object instance, String methodName, Object... params) throws Exception {
    return executeMethod(instance.getClass().getSuperclass(), instance, methodName, params);
}

public static <T> T executeMethod(Object instance, String methodName, Object... params) throws Exception {
    return executeMethod(instance.getClass(), instance, methodName, params);
}

@SuppressWarnings("unchecked")
public static <T> T executeMethod(Class clazz, Object instance, String methodName, Object... params) throws Exception {

    Method[] allMethods = clazz.getDeclaredMethods();

    if (allMethods != null && allMethods.length > 0) {

        Class[] paramClasses = Arrays.stream(params).map(p -> p != null ? p.getClass() : null).toArray(Class[]::new);

        for (Method method : allMethods) {
            String currentMethodName = method.getName();
            if (!currentMethodName.equals(methodName)) {
                continue;
            }
            Type[] pTypes = method.getParameterTypes();
            if (pTypes.length == paramClasses.length) {
                boolean goodMethod = true;
                int i = 0;
                for (Type pType : pTypes) {
                    if (!ClassUtils.isAssignable(paramClasses[i++], (Class<?>) pType)) {
                        goodMethod = false;
                        break;
                    }
                }
                if (goodMethod) {
                    method.setAccessible(true);
                    return (T) method.invoke(instance, params);
                }
            }
        }

        throw new MethodNotFoundException("There are no methods found with name " + methodName + " and params " +
            Arrays.toString(paramClasses));
    }

    throw new MethodNotFoundException("There are no methods found with name " + methodName);
}

该方法使用apache ClassUtils检查自动装箱的参数的兼容性


回答一个已有9年历史且浏览量超过90000并被接受的答案绝对没有意义。而是回答未回答的问题。
Anuraag Baishya

0

另外一个变种是使用功能非常强大的JOOR库https://github.com/jOOQ/jOOR

MyObject myObject = new MyObject()
on(myObject).get("privateField");  

它允许修改任何字段(如最终静态常量)并调用yne受保护的方法,而无需在继承层次结构中指定具体的类

<!-- https://mvnrepository.com/artifact/org.jooq/joor-java-8 -->
<dependency>
     <groupId>org.jooq</groupId>
     <artifactId>joor-java-8</artifactId>
     <version>0.9.7</version>
</dependency>

0

您可以使用Manifold的 @Jailbreak进行直接的类型安全的Java反射:

@Jailbreak Foo foo = new Foo();
foo.callMe();

public class Foo {
    private void callMe();
}

@Jailbreakfoo在编译器中解锁局部变量,以直接访问Foo层次结构中的所有成员。

同样,您可以将jailbreak()扩展方法一次性使用:

foo.jailbreak().callMe();

通过该jailbreak()方法,您可以访问Foo的层次结构中的任何成员。

在这两种情况下,编译器都会像公共方法一样安全地为您解决方法调用,而Manifold会在后台为您生成有效的反射代码。

或者,如果类型不是静态已知的,则可以使用结构化类型来定义类型可以满足的接口,而不必声明其实现。该策略保持类型安全,并避免了与反射和代理代码相关的性能和身份问题。

了解有关歧管的更多信息。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.