Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/374.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 有没有办法调用私有方法?_Java_Reflection_Private - Fatal编程技术网

Java 有没有办法调用私有方法?

Java 有没有办法调用私有方法?,java,reflection,private,Java,Reflection,Private,我有一个类,它使用XML和反射将对象返回到另一个类 通常,这些对象是外部对象的子字段,但有时我希望动态生成这些对象。我试过这样的方法,但没有用。我相信这是因为Java不允许您访问private方法进行反射 Element node = outerNode.item(0); String methodName = node.getAttribute("method"); String objectName = node.getAttribute("object"); if ("SomeObjec

我有一个类,它使用XML和反射将
对象
返回到另一个类

通常,这些对象是外部对象的子字段,但有时我希望动态生成这些对象。我试过这样的方法,但没有用。我相信这是因为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
。我可以通过将方法
公开
,或者创建另一个类来派生它来解决这个问题


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

使用
getDeclaredMethod()
获取private-method对象,然后使用
method.setAccessible()
允许实际调用它

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

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);
Method method = object.getClass().getDeclaredMethod(methodName);
method.setAccessible(true);
Object r = method.invoke(object);

这里有几个警告。首先,
getDeclaredMethod
将只查找在当前
类中声明的方法,而不是从超类型继承的方法。因此,如有必要,向上遍历具体的类层次结构。其次,
SecurityManager
可以阻止使用
setAccessible
方法。因此,它可能需要作为
PrivilegedAction
(使用
AccessController
Subject
)运行。

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

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;
    }
方法concatString可以作为

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

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

@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);
}
@SuppressWarnings(“未选中”)
公共静态T executeSuperMethod(对象实例、字符串方法名、对象…参数)引发异常{
返回executeMethod(instance.getClass().getSuperclass(),instance,methodName,params);
}
公共静态T executeMethod(对象实例、字符串方法名、对象…参数)引发异常{
返回executeMethod(instance.getClass(),instance,methodName,params);
}
@抑制警告(“未选中”)
公共静态T executeMethod(类clazz、对象实例、字符串methodName、对象…参数)引发异常{
方法[]allMethods=clazz.getDeclaredMethods();
if(allMethods!=null&&allMethods.length>0){
Class[]paramClasses=Arrays.stream(params).map(p->p!=null?p.getClass():null).toArray(Class[]::new);
对于(方法:所有方法){
字符串currentMethodName=method.getName();
如果(!currentMethodName.equals(methodName)){
继续;
}
Type[]pTypes=method.getParameterTypes();
if(pTypes.length==paramClasses.length){
布尔goodMethod=true;
int i=0;
对于(类型pType:pTypes){
if(!ClassUtils.isAssignable(paramClasses[i++],(Class)pType)){
goodMethod=false;
打破
}
}
if(goodMethod){
方法setAccessible(true);
return(T)method.invoke(instance,params);
}
}
}
抛出new MethodNotFoundException(“未找到名为“+methodName+”且参数为”的方法”+
toString(paramClasses));
}
抛出new MethodNotFoundException(“没有找到名为“+methodName”的方法);
}

方法使用ApacheClassutils来检查自动装箱参数的兼容性

还有一个变体正在使用非常强大的JOOR库

它允许修改任何字段,如final static constants,并调用受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>

org.jooq
joor-java-8
0.9.7

您可以使用Spring()的ReflectionTestUtils执行此操作

示例:如果您有一个具有私有方法的类
square(intx)

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

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

public class Foo {
    private void callMe();
}
@Jailbreak
解锁编译器中的
foo
局部变量,以便直接访问
foo
层次结构中的所有成员

类似地,您可以使用越狱()扩展方法进行一次性使用:

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

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

或者,如果类型不是静态已知的,则可以使用定义类型可以满足的接口,而无需声明其实现。此策略维护类型安全,并避免与反射和代理代码相关的性能和标识问题


了解有关的更多信息。

过去我这样做时,在调用方法后也调用了method.setAccessible(false),但我不知道这是否必要。不,当设置可访问性时,它仅适用于该实例。只要不让特定的方法对象从您的控制中逃逸,它是安全的。那么,如果可以从类外部调用私有方法,那么使用私有方法有什么意义呢?还要确保调用
getDeclaredMethod()
,而不仅仅是
getMethod()
-这对私有方法不起作用。@PeterAjtai很抱歉反应太晚,但请这样想:现在大多数人都锁上门,尽管他们知道锁可能会被轻微地打破或完全绕过。为什么?因为这有助于让大多数诚实的人保持诚实。您可以认为
private
access扮演着类似的角色
Calculator calculator = new Calculator();
ReflectionTestUtils.invokeMethod(calculator,"square",10);
@Jailbreak Foo foo = new Foo();
foo.callMe();

public class Foo {
    private void callMe();
}
foo.jailbreak().callMe();