我在第三方JAR中有一个设计很差的类,我需要访问它的一个私有字段。例如, 为什么我需要选择私人领域是必要的?
class IWasDesignedPoorly {
private Hashtable stuffIWant;
}
IWasDesignedPoorly obj = ...;
如何使用反射来获取stuffIWant的值?
我在第三方JAR中有一个设计很差的类,我需要访问它的一个私有字段。例如, 为什么我需要选择私人领域是必要的?
class IWasDesignedPoorly {
private Hashtable stuffIWant;
}
IWasDesignedPoorly obj = ...;
如何使用反射来获取stuffIWant的值?
当前回答
您需要完成以下操作:
private static Field getField(Class<?> cls, String fieldName) {
for (Class<?> c = cls; c != null; c = c.getSuperclass()) {
try {
final Field field = c.getDeclaredField(fieldName);
field.setAccessible(true);
return field;
} catch (final NoSuchFieldException e) {
// Try parent
} catch (Exception e) {
throw new IllegalArgumentException(
"Cannot access field " + cls.getName() + "." + fieldName, e);
}
}
throw new IllegalArgumentException(
"Cannot find field " + cls.getName() + "." + fieldName);
}
其他回答
试试Apache common -lang3中的FieldUtils:
FieldUtils.readField(object, fieldName, true);
附注:在我看来,反思是邪恶的。
使用Soot Java Optimization框架直接修改字节码。 http://www.sable.mcgill.ca/soot/
Soot完全是用Java编写的,并且适用于新的Java版本。
反射不是解决问题的唯一方法(它是访问类/组件的私有功能/行为)
另一种解决方案是从.jar中提取类,使用(比如)Jode或Jad反编译它,更改字段(或添加一个访问器),然后根据原始的.jar重新编译它。然后在类路径中将新的.class放在.jar的前面,或者将其重新插入到.jar中。(jar工具允许你提取和重新插入到一个现有的。jar)
如下所述,这解决了访问/更改私有状态的更广泛问题,而不是简单地访问/更改字段。
当然,这要求.jar不能被签名。
Java 9引入了变量句柄。您可以使用它们访问类的私有字段。
示例代码如下所示:
var lookup = MethodHandles.lookup();
var handle = MethodHandles
.privateLookupIn(IWasDesignedPoorly.class, lookup)
.findVarHandle(IWasDesignedPoorly.class, "stuffIWant", Hashtable.class);
var value = handle.get(obj);
使用Lookup和VarHandle对象作为静态final字段也是可取的。
正如oxbow_lakes提到的,您可以使用反射来绕过访问限制(假设您的SecurityManager允许您)。
也就是说,如果这门课设计得如此糟糕,以至于你不得不求助于这样的伎俩,也许你应该寻找另一种选择。当然,这个小技巧现在可能会为你节省几个小时,但它会让你付出多少代价呢?