Answers:
为了访问私有字段,您需要从类的声明字段中获取它们,然后使其可访问:
Field f = obj.getClass().getDeclaredField("stuffIWant"); //NoSuchFieldException
f.setAccessible(true);
Hashtable iWantThis = (Hashtable) f.get(obj); //IllegalAccessException
编辑:正如aperkins所说,访问字段,将字段设置为可访问并获取值都可能引发Exception
s,尽管上面需要注释的唯一检查异常。
在NoSuchFieldException
如果你问一个字段由不符合声明的字段的名称将被抛出。
obj.getClass().getDeclaredField("misspelled"); //will throw NoSuchFieldException
该IllegalAccessException
会如果字段是不可访问(被抛出例如,如果是私人和通过失踪了尚未作出访问f.setAccessible(true)
线。
该RuntimeException
可抛出s为要么SecurityException
S(如果JVM的SecurityManager
将不允许你改变一个字段的可访问性),或IllegalArgumentException
S,如果你尝试接入领域的对象不是字段的类的类型上:
f.get("BOB"); //will throw IllegalArgumentException, as String is of the wrong type
getDeclaredField()
如果在父类中定义了字段,则不会找到字段-您还必须遍历父类层次结构,getDeclaredField()
对每个父类进行调用,直到找到匹配项(之后可以调用setAccessible(true)
)或到达Object
。
private
领域产生影响。它可以防止意外访问。
FieldUtils
从apache commons-lang3 尝试:
FieldUtils.readField(object, fieldName, true);
反射不是解决问题的唯一方法(即访问类/组件的私有功能/行为)
一种替代解决方案是从.jar中提取类,使用(例如)Jode或Jad对其进行反编译,更改字段(或添加访问器),然后针对原始.jar对其进行重新编译。然后将新的.class放在.jar
classpath的前面,或将其重新插入到.jar
。(jar实用程序允许您提取并重新插入到现有的.jar中)
如下所述,这解决了访问/更改私有状态的广泛问题,而不是简单地访问/更改字段。
.jar
当然,这需要不签名。
使用Java中的Reflection,您可以private/public
将一个类的所有字段和方法访问到另一个类。但是,根据Oracle 文档 中的缺点部分,他们建议:
“由于反射允许代码执行在非反射代码中是非法的操作,例如访问私有字段和方法,因此使用反射可能会导致意外的副作用,这可能会使代码无法正常工作并可能破坏可移植性。反射代码破坏抽象,因此可能会随着平台的升级而改变行为”
这是下面的代码快照,以演示反射的基本概念
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所提到的,您可以使用反射来解决访问限制(假设您的SecurityManager可以让您)。
就是说,如果此类设计得如此糟糕以至于使您诉诸于此类黑客,那么也许您应该寻找替代方案。当然,这个小技巧现在可能可以为您节省几个小时,但是您要付出多少代价呢?
使用Soot Java Optimization框架直接修改字节码。 http://www.sable.mcgill.ca/soot/
Soot完全用Java编写,并且可以使用新的Java版本。
您需要执行以下操作:
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提供了一些方便的工具,可以在不费吹灰之力的情况下为您提供帮助。它被描述为“用于单元和集成测试方案”。还有一个类似的类,名为ReflectionUtils,但是被描述为“仅供内部使用” -有关此含义的解释,请参见此答案。
要解决发布的示例:
Hashtable iWantThis = (Hashtable)ReflectionTestUtils.getField(obj, "stuffIWant");
ReflectionTestUtils
根据我的经验,一直使用该示例,在测试情况下,我只需要做这种事情即可。)
只是关于反射的附加说明:我观察到在某些特殊情况下,当不同包中存在多个具有相同名称的类时,在最上面的答案中使用的反射可能无法从对象中选择正确的类。因此,如果您知道对象的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);
(这是对我不起作用的示例类)
您可以使用Manifold的 @JailBreak进行直接的类型安全的Java反射:
@JailBreak Foo foo = new Foo();
foo.stuffIWant = "123;
public class Foo {
private String stuffIWant;
}
@JailBreak
foo
在编译器中解锁局部变量,以直接访问Foo
层次结构中的所有成员。
同样,您可以将jailbreak()扩展方法一次性使用:
foo.jailbreak().stuffIWant = "123";
通过该jailbreak()
方法,您可以访问Foo
的层次结构中的任何成员。
在这两种情况下,编译器都会像公共字段一样安全地为您解析字段访问,而Manifold会在后台为您生成有效的反射代码。
发现有关歧管的更多信息。
使用XrayInterface工具非常容易。只需定义缺少的getter / setter,例如
interface BetterDesigned {
Hashtable getStuffIWant(); //is mapped by convention to stuffIWant
}
并对您设计不良的项目进行X射线检查:
IWasDesignedPoorly obj = new IWasDesignedPoorly();
BetterDesigned better = ...;
System.out.println(better.getStuffIWant());
在内部,这依赖于反射。
尝试解决该问题,您要设置/获取数据的数据量是您自己的类之一。
只要创建一个public setter(Field f, Object value)
和public Object getter(Field f)
为。您甚至可以在这些成员函数中自行进行一些安全检查。例如,二传手:
class myClassName {
private String aString;
public set(Field field, Object value) {
// (A) do some checkings here for security
// (B) set the value
field.set(this, value);
}
}
当然,现在你必须找出java.lang.reflect.Field
对sString
在设置字段值之前。
我确实在通用的ResultSet到模型生成器中使用了此技术。