我如何阅读Java中的私人领域?

在第三方JAR我有一个devise不好的类,我需要访问它的一个私有字段。 例如,为什么我需要select私人领域是必要的?

 class IWasDesignedPoorly { private Hashtable stuffIWant; } IWasDesignedPoorly obj = ...; 

我如何使用reflection来获得stuffIWant的价值?

为了访问私有字段,您需要从类的声明字段中获取它们,然后使其可访问:

 Field f = obj.getClass().getDeclaredField("stuffIWant"); //NoSuchFieldException f.setAccessible(true); Hashtable iWantThis = (Hashtable) f.get(obj); //IllegalAccessException 

编辑 :由aperkins评论,既访问该字段,将其设置为可访问和检索的价值将全部抛出Exception s,虽然唯一检查的exception,你需要注意上面评论。

如果您通过与声明字段不对应的名称来请求字段,则会引发NoSuchFieldException

 obj.getClass().getDeclaredField("misspelled"); //will throw NoSuchFieldException 

如果该字段不可访问,则会抛出IllegalAccessExceptionexception(例如,如果该字段是私有的,并且由于缺lessf.setAccessible(true)行而无法访问f.setAccessible(true)

可能抛出的RuntimeExceptionSecurityException (如果JVM的SecurityManager不允许你改变一个字段的可访问性),或者IllegalArgumentException ,如果你试图访问一个不是字段类types的对象的字段:

 f.get("BOB"); //will throw IllegalArgumentException, as String is of the wrong type 

从apache commons-lang3尝试FieldUtils:

 FieldUtils.readField(object, fieldName, true); 

reflection并不是解决问题的唯一方法(即访问类/组件的私有function/行为)

另一种解决scheme是从.jar中提取类,使用(比如说) Jode或Jad进行反编译,更改字段(或者添加一个访问器),然后重新编译为原始的.jar文件。 然后将新的.class放在类path的.jar之前,或者将其重新插入到.jar 。 (jar实用程序允许您提取并重新插入现有的.jar)

如下所述,这解决了访问/更改私人状态这一更广泛的问题,而不是简单地访问/更改一个字段。

这需要.jar当然不能被签名。

另一个尚未提及的选项:使用Groovy 。 Groovy允许您访问私有实例variables,作为语言devise的副作用。 无论你是否有一个领域的吸气,你可以使用

 def obj = new IWasDesignedPoorly() def hashTable = obj.getStuffIWant() 

在Java中使用Reflection可以访问一个类的所有private/public字段和方法。但是按照Oracle 文档中的缺陷部分,他们build议:

“由于reflection允许代码执行在非reflection代码中非法的操作,例如访问专用字段和方法,所以reflection的使用可能会导致意想不到的副作用,这可能会导致代码function失常,并可能破坏可移植性。打破抽象,因此可能会改变平台的升级行为“

下面是一些代码片段来演示Reflection的基本概念

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())); } } } 

希望它会有所帮助。

正如oxbow_lakes所提到的,你可以使用reflection来解决访问限制(假设你的SecurityManager会让你)。

也就是说,如果这个class级devise的太差,让你诉诸这样的嘲弄,也许你应该寻找替代scheme。 当然,这个小小的破解可能会让你节省几个小时的时间,但是要花多less钱呢?

使用Soot Java Optimization框架直接修改字节码。 http://www.sable.mcgill.ca/soot/

Soot完全是用Java编写的,可以与新的Java版本一起使用。

关于reflection的附加说明:在某些特殊情况下,我观​​察到,当几个具有相同名称的类存在于不同的包中时,顶级答案中使用的reflection可能无法从对象中select正确的类。 所以,如果你知道对象的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); } 

如果使用Spring, ReflectionTestUtils提供了一些方便的工具,只需要很less的工作就可以帮到您。 它被描述为“用于单元和集成testing场景” 。 还有一个类似ReflectionUtils的类,但是这个类被描述为“仅供内部使用” – 请参阅这个答案来解释这个意思。

要解决发布的示例:

 Hashtable iWantThis = (Hashtable)ReflectionTestUtils.getField(obj, "stuffIWant");