我如何阅读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
如果该字段不可访问,则会抛出IllegalAccessException
exception(例如,如果该字段是私有的,并且由于缺lessf.setAccessible(true)
行而无法访问f.setAccessible(true)
。
可能抛出的RuntimeException
是SecurityException
(如果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");