Fra*_*ger 466 java reflection field private class
我在第三方设计的设计很差JAR,我需要访问其中一个私有字段.例如,为什么我需要选择私人领域是否有必要?
class IWasDesignedPoorly {
private Hashtable stuffIWant;
}
IWasDesignedPoorly obj = ...;
Run Code Online (Sandbox Code Playgroud)
我如何使用反射来获得价值stuffIWant?
oxb*_*kes 649
要访问私有字段,您需要从类的声明字段中获取它们,然后使它们可访问:
Field f = obj.getClass().getDeclaredField("stuffIWant"); //NoSuchFieldException
f.setAccessible(true);
Hashtable iWantThis = (Hashtable) f.get(obj); //IllegalAccessException
Run Code Online (Sandbox Code Playgroud)
编辑:正如aperkins评论的那样,访问该字段,将其设置为可访问并检索值都将抛出Exceptions,尽管上面评论了您需要注意的唯一已检查的异常.
在NoSuchFieldException如果你问一个字段由不符合声明的字段的名称将被抛出.
obj.getClass().getDeclaredField("misspelled"); //will throw NoSuchFieldException
Run Code Online (Sandbox Code Playgroud)
该IllegalAccessException会如果字段是不可访问(被抛出例如,如果是私人和通过失踪了尚未作出访问f.setAccessible(true)线.
该RuntimeException可抛出s为要么SecurityExceptionS(如果JVM的SecurityManager将不允许你改变一个字段的可访问性),或IllegalArgumentExceptionS,如果你尝试的对象不是字段的类的类型上访问该字段:
f.get("BOB"); //will throw IllegalArgumentException, as String is of the wrong type
Run Code Online (Sandbox Code Playgroud)
yeg*_*256 147
试试FieldUtilsapache commons-lang3:
FieldUtils.readField(object, fieldName, true);
Run Code Online (Sandbox Code Playgroud)
Bri*_*new 26
反射不是解决问题的唯一方法(即访问类/组件的私有功能/行为)
另一种解决方案是从.jar中提取类,使用(比如)Jode或Jad对其进行反编译,更改字段(或添加访问器),然后针对原始.jar重新编译它.然后将新的.class放在.jar类路径中,或者将其重新插入到类路径中.jar.(jar实用程序允许您提取并重新插入现有的.jar)
如下所述,这解决了访问/更改私有状态的更广泛问题,而不是简单地访问/更改字段.
.jar当然,这要求不要签名.
luc*_*cas 18
另一个尚未提及的选项:使用Groovy.Groovy允许您访问私有实例变量,作为语言设计的副作用.无论你是否有野外吸气器,你都可以使用
def obj = new IWasDesignedPoorly()
def hashTable = obj.getStuffIWant()
Run Code Online (Sandbox Code Playgroud)
使用Java中的Reflection,您可以访问private/public一个类到另一个类的所有字段和方法.但是根据Oracle 文档 中的缺点,他们建议:
"由于反射允许代码执行在非反射代码中非法的操作,例如访问私有字段和方法,使用反射会导致意外的副作用,这可能导致代码功能失常并可能破坏可移植性.反射代码打破抽象,因此可能会通过升级平台来改变行为"
下面是代码snapts,用于演示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");
}
}
Run Code Online (Sandbox Code Playgroud)
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()));
}
}
}
Run Code Online (Sandbox Code Playgroud)
希望它会有所帮助.
Java 9 引入了变量句柄。您可以使用它们访问类的私有字段。
您的示例的代码如下所示:
var lookup = MethodHandles.lookup();
var handle = MethodHandles
.privateLookupIn(IWasDesignedPoorly.class, lookup)
.findVarHandle(IWasDesignedPoorly.class, "stuffIWant", Hashtable.class);
var value = handle.get(obj);
Run Code Online (Sandbox Code Playgroud)
还建议使用Lookup和VarHandle对象作为static final字段。
正如oxbow_lakes所提到的,你可以使用反射来绕过访问限制(假设你的SecurityManager会让你).
也就是说,如果这个类的设计非常糟糕,以至于它让你诉诸于这样的hackery,也许你应该寻找替代方案.当然,这个小小的黑客可能会为你节省几个小时,但是你需要花多少钱呢?
| 归档时间: |
|
| 查看次数: |
274106 次 |
| 最近记录: |