我试图学习反射,但我遇到了这个IllegalAccessException。请参阅以下代码:
public class ReflectionTest
{
public static void main(String[] args)
{
Set<String> myStr = new HashSet<String>();
myStr.add("obj1");
Iterator itr = myStr.iterator();
Method mtd = itr.getClass().getMethod("hasNext");
System.out.println(m.invoke(it));
}
}
当我尝试运行此程序时,我得到以下内容:
Exception in thread "main" IllegalAccessException
我不明白发生了什么。有任何想法吗?提前谢谢。
答案 0 :(得分:32)
您需要禁止Java语言访问检查,以便使用setAccessible(true)反射性地调用另一个类中的私有方法:
Method mtd= itr.getClass().getMethod("hasNext");
if(!mtd.isAccessible()) {
mtd.setAccessible(true);
}
此外,当启用SecurityManager时,我们需要额外的权限来调用setAccessible(true)。否则,我们得到:
C:\ReflectionTest>java -Djava.security.manager CallFoo
Exception in thread "main" java.security.AccessControlException: access denied (java.lang.reflect.ReflectPermission suppressAccessChecks)
at java.security.AccessControlContext.checkPermission(AccessControlContext.java:264)
at java.security.AccessController.checkPermission(AccessController.java:427)
at java.lang.SecurityManager.checkPermission(SecurityManager.java:532)
at java.lang.reflect.AccessibleObject.setAccessible(AccessibleObject.java:107)
at CallFoo.main(CallFoo.java:8)
我们只希望将此suppressAccessChecks权限授予受信任的代码源,绝对不会将其授予调用堆栈中的所有类。所以我们将修改CallFoo.java:
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
public class CallFoo {
public static void main(String args[]) throws Exception {
doCallFoo();
}
public static void doCallFoo() throws IllegalAccessException, ClassNotFoundException, NoSuchMethodException,
InvocationTargetException, InstantiationException, PrivilegedActionException {
Class fooClass = Class.forName("Foo");
final Foo foo = (Foo) fooClass.newInstance();
final Method helloMethod = fooClass.getDeclaredMethod("hello");
AccessController.doPrivileged(new PrivilegedExceptionAction() {
public Object run() throws Exception {
if(!helloMethod.isAccessible()) {
helloMethod.setAccessible(true);
}
helloMethod.invoke(foo);
return null;
}
});
}
}
答案 1 :(得分:8)
麻烦的代码就是:
itr.getClass().getMethod
您可能希望在hasNext
课程中Iterator
。您编写的是HashMap.KeyIterator
类,根据Java语言访问说明符(或至少是反射使用的JDK 1.0的粗略解释),您的代码无法使用它。
改为使用:
Iterator.class.getMethod
(如果不是出于学习目的,请远离反思。)
答案 2 :(得分:2)
您无法访问它,因为Iterator是一个私有的内部类。可以找到更多解释here。
答案 3 :(得分:0)
很明显,您当前正在执行的方法无法访问名为 hasNext
的方法,例如private
或protected
。您可以尝试使用method.setAccessible(true);
您security manager
中可能还有一些restrictions defined(如果使用的话,linux
,可能已包含在默认范围内分发java包)。
[编辑] 事实证明,Tom Hawtin确定了正确的根本原因。你确实在HashMap.KeyIterator
上运作。虽然解决方案是使用Iterator.class
而不是itr.getClass()
,但您仍然可以使用setAccessible(true)
启用对它的访问权限。
答案 4 :(得分:0)
我怀疑你应该使用getDeclaredMethod(以及其他问题)。我不打算记住Reflection API的详细信息(它们是针对编译器的!),但在您的情况下,将您的代码与dp4j生成的代码进行比较:
$ javac -Averbose=true -All -cp dp4j-1.2-SNAPSHOT-jar-with-dependencies.jar ReflectionTest.java
ReflectionTest.java:6: Note:
import java.util.*;
public class ReflectionTest {
public ReflectionTest() {
super();
}
@com.dp4j.Reflect()
public static void main(String[] args) throws java.lang.ClassNotFoundException, java.lang.NoSuchFieldException, java.lang.IllegalAccessException, java.lang.NoSuchMethodException, java.lang.reflect.InvocationTargetException, java.lang.InstantiationException, java.lang.IllegalArgumentException {
final java.lang.reflect.Constructor hashSetConstructor = Class.forName("java.util.HashSet").getDeclaredConstructor();
hashSetConstructor.setAccessible(true);
Set<String> myStr = (.java.util.Set<.java.lang.String>)hashSetConstructor.newInstance();
final java.lang.reflect.Method addWithEMethod = Class.forName("java.util.Set").getDeclaredMethod("add", .java.lang.Object.class);
addWithEMethod.setAccessible(true);
addWithEMethod.invoke(myStr, new .java.lang.Object[1][]{"obj1"});
final java.lang.reflect.Method iteratorMethod = Class.forName("java.util.Set").getDeclaredMethod("iterator");
iteratorMethod.setAccessible(true);
Iterator itr = (.java.util.Iterator)iteratorMethod.invoke(myStr);
final java.lang.reflect.Method hasNextMethod = Class.forName("java.util.Iterator").getDeclaredMethod("hasNext");
hasNextMethod.setAccessible(true);
final java.lang.reflect.Method printlnWithbooleanMethod = Class.forName("java.io.PrintStream").getDeclaredMethod("println", .java.lang.Boolean.TYPE);
printlnWithbooleanMethod.setAccessible(true);
printlnWithbooleanMethod.invoke(System.out, new .java.lang.Object[1][]{hasNextMethod.invoke(itr)});
}
}
public static void main(String[] args)
^
...
$ java ReflectionTest
true
您需要做的唯一更改是使用@ com.dp4j.Reflect注释主要方法:
$ vim ReflectionTest.java
import java.util.*;
public class ReflectionTest
{
@com.dp4j.Reflect
public static void main(String[] args)
{
Set<String> myStr = new HashSet<String>();
myStr.add("obj1");
Iterator itr = myStr.iterator();
// Method mtd = itr.getClass().getMethod("hasNext");
System.out.println(itr.hasNext());
}
}
注意:这只适用于dp4j-1.2-SNAPSHOT(我刚为它添加了支持)。如果您不使用Maven,请从here下载jar。您可以找到问题here的测试用例。