如何从Java中的不同类读取私有字段的值?
我在第三方如何从Java中的不同类读取私有字段的值?,java,class,reflection,field,private,Java,Class,Reflection,Field,Private,我在第三方JAR中有一个设计糟糕的类,我需要访问它的私有字段之一。例如 为什么我需要选择私人领域是必要的 class IWasDesignedPoorly { private Hashtable stuffIWant; } IWasDesignedPoorly obj = ...; 如何使用反射来获取stuffIWant的值?要访问私有字段,需要从类的声明字段中获取它们,然后使其可访问: Field f = obj.getClass().getDeclaredField("stuff
JAR
中有一个设计糟糕的类,我需要访问它的私有字段之一。例如
为什么我需要选择私人领域是必要的
class IWasDesignedPoorly {
private Hashtable stuffIWant;
}
IWasDesignedPoorly obj = ...;
如何使用反射来获取
stuffIWant
的值?要访问私有字段,需要从类的声明字段中获取它们,然后使其可访问:
Field f = obj.getClass().getDeclaredField("stuffIWant"); //NoSuchFieldException
f.setAccessible(true);
Hashtable iWantThis = (Hashtable) f.get(obj); //IllegalAccessException
编辑:正如aperkins所评论的那样,访问字段、将其设置为可访问以及检索值都会引发异常
s,尽管您需要注意的唯一已检查的异常已在上面进行了评论
如果您使用与声明的字段不对应的名称请求字段,则会抛出NoSuchFieldException
obj.getClass().getDeclaredField("misspelled"); //will throw NoSuchFieldException
如果该字段不可访问(例如,如果该字段是私有的且未通过遗漏f.setAccessible(true)
行使其可访问,则将抛出IllegalAccessException
可能引发的RuntimeException
s要么是SecurityException
s(如果JVM的SecurityManager
不允许您更改字段的可访问性),要么是IllegalArgumentException
s,如果您尝试访问非字段类类型的对象上的字段:
f.get("BOB"); //will throw IllegalArgumentException, as String is of the wrong type
正如oxbow_lakes提到的,您可以使用反射绕过访问限制(假设您的安全管理器允许)
这就是说,如果这个类的设计如此糟糕,以至于你不得不求助于这种黑客行为,也许你应该寻找一种替代方法。当然,这个小黑客现在可能会为你节省几个小时,但它会花费你多少钱呢?反思并不是解决问题的唯一方法(用于访问类/组件的私有功能/行为) 另一种解决方案是从.jar中提取类,使用(比如)或反编译它,更改字段(或添加访问器),然后根据原始的.jar重新编译它。然后将新的.class放在类路径中
.jar
的前面,或者将其重新插入.jar
中。(jar实用程序允许您提取并重新插入现有的.jar)
如下所述,这解决了访问/更改私有状态的更广泛问题,而不是简单地访问/更改字段
当然,这需要
.jar
不被签名。使用Socome Java优化框架直接修改字节码。
烟尘完全是用Java编写的,可以与新的Java版本一起使用。还有一个尚未提及的选项:使用Groovy。Groovy允许您访问私有实例变量,这是语言设计的一个副作用。无论您是否有该字段的getter,您都可以使用
def obj = new IWasDesignedPoorly()
def hashTable = obj.getStuffIWant()
从apache commons-lang3尝试:
FieldUtils.readField(object, fieldName, true);
使用Java中的反射可以访问一个类到另一个类的所有
私有/公共
字段和方法。但是根据缺陷部分中的Oracle,他们建议:
"由于反射允许代码执行非反射代码中非法的操作,例如访问私有字段和方法,因此使用反射可能会产生意外的副作用,这可能导致代码功能失调,并可能破坏可移植性。反射代码破坏抽象,因此可能会使用upgra更改行为“平台的安全性”
下面是演示反射的基本概念的代码快照
Reflection1.java
public class Reflection1{
private int i = 10;
public void methoda()
{
System.out.println("method1");
}
public void methodb()
{
System.out.println("method2");
}
public void methodc()
{
System.out.println("method3");
}
}
Reflection2.java
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Reflection2{
public static void main(String ar[]) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException
{
Method[] mthd = Reflection1.class.getMethods(); // for axis the methods
Field[] fld = Reflection1.class.getDeclaredFields(); // for axis the fields
// Loop for get all the methods in class
for(Method mthd1:mthd)
{
System.out.println("method :"+mthd1.getName());
System.out.println("parametes :"+mthd1.getReturnType());
}
// Loop for get all the Field in class
for(Field fld1:fld)
{
fld1.setAccessible(true);
System.out.println("field :"+fld1.getName());
System.out.println("type :"+fld1.getType());
System.out.println("value :"+fld1.getInt(new Reflaction1()));
}
}
}
希望这会有所帮助。关于反射的另一个注意事项:我观察到在一些特殊情况下,当不同的包中存在几个同名的类时,顶部答案中使用的反射可能无法从对象中选择正确的类。因此,如果您知道对象的package.class是什么,那么最好使用按如下方式访问其私有字段值:
org.deeplearning4j.nn.layers.BaseOutputLayer ll = (org.deeplearning4j.nn.layers.BaseOutputLayer) model.getLayer(0);
Field f = Class.forName("org.deeplearning4j.nn.layers.BaseOutputLayer").getDeclaredField("solver");
f.setAccessible(true);
Solver s = (Solver) f.get(ll);
(这是一个不适用于我的示例类)您需要执行以下操作:
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);
}
private静态字段getField(类cls,字符串fieldName){
for(类c=cls;c!=null;c=c.getSuperclass()){
试一试{
最终字段=c.getDeclaredField(字段名);
字段。setAccessible(true);
返回字段;
}捕获(最终NOSUCHFIELD异常e){
//试试父母
}捕获(例外e){
抛出新的IllegalArgumentException(
“无法访问字段”+cls.getName()+“+fieldName,e);
}
}
抛出新的IllegalArgumentException(
“找不到字段”+cls.getName()+“+fieldName”);
}
如果使用弹簧:
在测试上下文中,提供了一些简便的工具,可以在这里以最小的工作量提供帮助。它被描述为“用于单元和集成测试场景”
在非测试上下文中也有一个类似的类,名为,但被描述为“仅供内部使用”-请参阅,以获得对其含义的良好解释
要解决原始帖子中的示例:
Hashtable iWantThis = (Hashtable)ReflectionTestUtils.getField(obj, "stuffIWant");
您可以使用@JailBreak进行直接的、类型安全的Java反射:
@JailBreak Foo foo = new Foo();
foo.stuffIWant = "123;
public class Foo {
private String stuffIWant;
}
@JailBreak
解锁编译器中的foo
局部变量,以便直接访问foo
层次结构中的所有成员
类似地,您可以使用越狱()扩展方法进行一次性使用:
foo.jailbreak().stuffIWant = "123";
通过jailbreak()
方法,您可以访问Foo
层次结构中的任何成员
在这两种情况下,编译器都会安全地解析您键入的字段访问权限,就像解析公共字段一样,而流形会在h下为您生成有效的反射代码
interface BetterDesigned {
Hashtable getStuffIWant(); //is mapped by convention to stuffIWant
}
IWasDesignedPoorly obj = new IWasDesignedPoorly();
BetterDesigned better = ...;
System.out.println(better.getStuffIWant());