如何从Java中的不同类读取私有字段的值?
我在第 3 方 JAR
中有一个设计不佳的类,我需要访问它的一个私有字段。 例如, 为什么我需要选择私人领域?有必要吗?
class IWasDesignedPoorly {
private Hashtable stuffIWant;
}
IWasDesignedPoorly obj = ...;
如何使用反射来获取 stuffIWant
的值?
I have a poorly designed class in a 3rd-party JAR
and I need to access one of its private fields. For example,
why should I need to choose private field is it necessary?
class IWasDesignedPoorly {
private Hashtable stuffIWant;
}
IWasDesignedPoorly obj = ...;
How can I use reflection to get the value of stuffIWant
?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(14)
使用Java中的反射,您可以访问一个类到另一个类的所有
私有/公共
字段和方法。但是根据Oracle文档在缺点部分中,他们建议:< em>“由于反射允许代码执行在非反射代码中非法的操作,例如访问私有字段和方法,因此使用反射可能会导致意外的副作用,这可能会导致代码功能失调并可能破坏可移植性。反射代码破坏了抽象,因此可能会随着平台的升级而改变行为”
下面的代码片段演示了 Reflection 的基本概念
Reflection1.java
Reflection2.java
希望它会有所帮助。
Using the Reflection in Java you can access all the
private/public
fields and methods of one class to another .But as per the Oracle documentation in the section drawbacks they recommended that :"Since reflection allows code to perform operations that would be illegal in non-reflective code, such as accessing private fields and methods, the use of reflection can result in unexpected side-effects, which may render code dysfunctional and may destroy portability. Reflective code breaks abstractions and therefore may change behavior with upgrades of the platform"
here is following code snapts to demonstrate basic concepts of Reflection
Reflection1.java
Reflection2.java
Hope it will help.
Java 9 引入了变量句柄。 您可以使用它们访问类的私有字段。
您的示例的代码如下所示:
还建议使用
Lookup
和VarHandle
对象作为static final
字段。Java 9 introduced Variable Handles. You can access a private field of a class using them.
The code for your example will look like following:
It is also advisable to use
Lookup
andVarHandle
objects asstatic final
fields.正如 oxbow_lakes 提到的,您可以使用反射来绕过访问限制(假设您的 SecurityManager 允许)。
也就是说,如果这个类的设计如此糟糕,以至于让您不得不求助于这种黑客手段,也许您应该寻找替代方案。 当然,这个小窍门现在可能会为您节省几个小时,但是它会花费您多少钱呢?
As oxbow_lakes mentions, you can use reflection to get around the access restrictions (assuming your SecurityManager will let you).
That said, if this class is so badly designed that it makes you resort to such hackery, maybe you should look for an alternative. Sure this little hack might be saving you a few hours now, but how much will it cost you down the road?
关于反射的附加说明:我在某些特殊情况下观察到,当不同包中存在多个具有相同名称的类时,顶部答案中使用的反射可能无法从对象中选择正确的类。 因此,如果您知道该对象的 package.class 是什么,那么最好按如下方式访问其私有字段值:(
这是对我不起作用的示例类)
Just an additional note about reflection: I have observed in some special cases, when several classes with the same name exist in different packages, that reflection as used in the top answer may fail to pick the correct class from the object. So if you know what is the package.class of the object, then it's better to access its private field values as follows:
(This is the example class that was not working for me)
您需要执行以下操作:
You need to do the following:
您可以使用 jOOR 来实现此目的。
You can use jOOR for that.
使用Soot Java Optimization框架直接修改字节码。
http://www.sable.mcgill.ca/soot/
Soot 完全是用Java 并适用于新的 Java 版本。
Use the Soot Java Optimization framework to directly modify the bytecode.
http://www.sable.mcgill.ca/soot/
Soot is completely written in Java and works with new Java versions.
如果使用 Spring:
在测试环境中,ReflectionTestUtils 提供了一些方便的工具,可以轻松地帮助解决此问题。 它被描述为“用于单元和集成测试场景”。
在非测试环境中,还有一个名为 ReflectionUtils 但这被描述为“仅供内部使用” - 请参阅这个答案可以很好地解释这意味着什么。
要解决原始帖子中的示例:
If using Spring:
In a testing context, ReflectionTestUtils provides some handy tools that can help out here with minimal effort. It's described as being "for use in unit and integration testing scenarios".
In a non-testing context, there is also a similar class named ReflectionUtils but this is described as "Only intended for internal use" - see this answer for a good interpretation of what this means.
To address the example in the original post:
使用工具 XrayInterface 非常容易。 只需定义缺少的 getter/setter,例如,
然后检查您设计不佳的项目:
在内部,这依赖于反射。
It is quite easy with the tool XrayInterface. Just define the missing getters/setters, e.g.
and xray your poor designed project:
Internally this relies on reflection.
尝试解决这种情况的问题,您要设置/获取数据的类是您自己的类之一。
只需为此创建一个
public setter(Field f, Object value)
和public Object getter(Field f)
即可。 您甚至可以在这些成员函数中自己进行一些安全检查。 例如对于setter:当然,现在您必须在设置字段值之前找到
sString
的java.lang.reflect.Field
。我确实在通用结果集到模型映射器和模型映射器中使用了这种技术。
Try to go around the problem for the case, the calass of which you want to set/get data is one of your own classes.
Just create a
public setter(Field f, Object value)
andpublic Object getter(Field f)
for that. You can even do some securoty check on your own inside theses member functions. E.g. for the setter:Of course, now you have to find out the
java.lang.reflect.Field
forsString
prior to setting of field value.I do use this technique in a generic ResultSet-to-and-from-model-mapper.
为了访问私有字段,您需要从类的声明字段中获取它们,然后使它们可访问:
编辑:正如aperkins所评论的那样em>,访问字段、将其设置为可访问以及检索值都可能抛出
异常
,尽管您需要注意的唯一已检查异常已在上面进行了注释。如果您请求的字段名称与声明的字段不对应,则会抛出
NoSuchFieldException
。如果该字段不可访问(例如,如果该字段是私有的并且未通过缺少
f.setAccessible(true)
行使其无法访问,则将引发IllegalAccessException
可能抛出的RuntimeException 是
SecurityException
(如果 JVM 的SecurityManager
不允许您更改字段的可访问性),或者IllegalArgumentException
,如果您尝试访问不属于字段类类型的对象上的字段:In order to access private fields, you need to get them from the class's declared fields and then make them accessible:
EDIT: as has been commented by aperkins, both accessing the field, setting it as accessible and retrieving the value can throw
Exception
s, although the only checked exceptions you need to be mindful of are commented above.The
NoSuchFieldException
would be thrown if you asked for a field by a name which did not correspond to a declared field.The
IllegalAccessException
would be thrown if the field was not accessible (for example, if it is private and has not been made accessible via missing out thef.setAccessible(true)
line.The
RuntimeException
s which may be thrown are eitherSecurityException
s (if the JVM'sSecurityManager
will not allow you to change a field's accessibility), orIllegalArgumentException
s, if you try and access the field on an object not of the field's class's type:尝试
FieldUtils
来自 Apache commons-lang3:PS 在我看来, 反思是邪恶的。
Try
FieldUtils
from Apache commons-lang3:P.S. In my opinion, reflection is evil.
反射不是解决问题的唯一方法(即访问类/组件的私有功能/行为)
另一种解决方案是从 .jar 中提取类,使用(例如)Jode 或 Jad,更改字段(或添加访问器),然后针对原始 .jar 重新编译它。 然后将新的 .class 放在类路径中的
.jar
之前,或者将其重新插入到.jar
中。 (jar 实用程序允许您提取并重新插入现有的 .jar)如下所述,这解决了访问/更改私有状态的更广泛问题,而不是简单地访问/更改字段。
当然,这要求
.jar
不被签名。Reflection isn't the only way to resolve your issue (which is to access the private functionality/behaviour of a class/component)
An alternative solution is to extract the class from the .jar, decompile it using (say) Jode or Jad, change the field (or add an accessor), and recompile it against the original .jar. Then put the new .class ahead of the
.jar
in the classpath, or reinsert it in the.jar
. (the jar utility allows you to extract and reinsert to an existing .jar)As noted below, this resolves the wider issue of accessing/changing private state rather than simply accessing/changing a field.
This requires the
.jar
not to be signed, of course.另一种尚未提及的选项:使用 Groovy。 Groovy 允许您访问私有实例变量,这是该语言设计的副作用。 无论你是否有该字段的吸气剂,你都可以使用
One other option that hasn't been mentioned yet: use Groovy. Groovy allows you to access private instance variables as a side effect of the design of the language. Whether or not you have a getter for the field, you can just use