使用反射设置私有字段值

时间:2015-09-22 12:31:52

标签: java reflection

我有两个课程:FatherChild

public class Father implements Serializable, JSONInterface {

    private String a_field;

    //setter and getter here

}

public class Child extends Father {
    //empty class
}

通过反射,我想在a_field类中设置Child

Class<?> clazz = Class.forName("Child");
Object cc = clazz.newInstance();

Field f1 = cc.getClass().getField("a_field");
f1.set(cc, "reflecting on life");
String str1 = (String) f1.get(cc.getClass());
System.out.println("field: " + str1);

但我有一个例外:

  

线程中的异常&#34; main&#34; java.lang.NoSuchFieldException:a_field

但如果我尝试:

Child child = new Child();
child.setA_field("123");

它有效。

使用setter方法我遇到同样的问题:

method = cc.getClass().getMethod("setA_field");
method.invoke(cc, new Object[] { "aaaaaaaaaaaaaa" });

6 个答案:

答案 0 :(得分:90)

要访问私有字段,您需要将Field::setAccessible设置为true。你可以把这个领域从超级阶段拉下来。此代码有效:

Class<?> clazz = Child.class;
Object cc = clazz.newInstance();

Field f1 = cc.getClass().getSuperclass().getDeclaredField("a_field");
f1.setAccessible(true);
f1.set(cc, "reflecting on life");
String str1 = (String) f1.get(cc);
System.out.println("field: " + str1);

答案 1 :(得分:30)

使用Apache commons-lang3:

FieldUtils.writeField(childInstance, "a_field", "Hello", true);

&#34; true&#34;迫使它设置,即使使用&#34; private&#34;。

答案 2 :(得分:4)

这个人也可以访问私人字段而无需做任何事情

import org.apache.commons.lang3.reflect.FieldUtils;
Object value = FieldUtils.readField(entity, fieldName, true);

答案 3 :(得分:3)

科特林版

使用以下扩展功能获取私有变量

fun <T : Any> T.getPrivateProperty(variableName: String): Any? {
    return javaClass.getDeclaredField(variableName).let { field ->
        field.isAccessible = true
        return@let field.get(this)
    }
}

设置私有变量值获取变量

fun <T : Any> T.setAndReturnPrivateProperty(variableName: String, data: Any): Any? {
    return javaClass.getDeclaredField(variableName).let { field ->
        field.isAccessible = true
        field.set(this, data)
        return@let field.get(this)
    }
}

获取变量用途:

val bool = <your_class_object>.getPrivateProperty("your_variable") as String

设置并获取变量用途:

val bool = <your_class_object>.setAndReturnPrivateProperty("your_variable", true) as Boolean
val str = <your_class_object>.setAndReturnPrivateProperty("your_variable", "Hello") as String

Java版本

public class RefUtil {

    public static Field setFieldValue(Object object, String fieldName, Object valueTobeSet) throws NoSuchFieldException, IllegalAccessException {
        Field field = getField(object.getClass(), fieldName);
        field.setAccessible(true);
        field.set(object, valueTobeSet);
        return field;
    }

    public static Object getPrivateFieldValue(Object object, String fieldName) throws NoSuchFieldException, IllegalAccessException {
        Field field = getField(object.getClass(), fieldName);
        field.setAccessible(true);
        return field.get(object);
    }

    private static Field getField(Class mClass, String fieldName) throws NoSuchFieldException {
        try {
            return mClass.getDeclaredField(fieldName);
        } catch (NoSuchFieldException e) {
            Class superClass = mClass.getSuperclass();
            if (superClass == null) {
                throw e;
            } else {
                return getField(superClass, fieldName);
            }
        }
    }
}

设置私人使用价值

RefUtil.setFieldValue(<your_class_object>, "your_variableName", newValue);

获取私人使用价值

Object value = RefUtil.getPrivateFieldValue(<your_class_object>, "your_variableName");

答案 4 :(得分:0)

您可以使用Manifold's @Jailbreak 进行直接的类型安全的Java反射:

@Jailbreak Child child = new Child();
child.a_field = "123;

@Jailbreak在编译器中解锁子局部变量,以直接访问Child层次结构中的所有成员。

类似地,您可以将 jailbreak()扩展方法用于一次性使用:

child.jailbreak().a_field = "123";

通过jailbreak()方法,您可以访问Child层次结构中的任何成员。

在这两种情况下,编译器都会像公共字段一样安全地为您解决字段访问,而Manifold会在后台为您生成有效的反射代码。

了解有关Manifold的更多信息。

答案 5 :(得分:-4)

根据Class.getField的Javadoc(强调我的):

  

返回一个Field对象,该对象反映此Class对象所代表的类或接口的指定公共成员字段

此方法仅返回公共字段。由于a_field是私有的,因此无法找到。

这是一个有效的代码:

public class Main {

    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("Child");
        Object cc = clazz.newInstance();

        Field f1 = cc.getClass().getField("a_field");
        f1.set(cc, "reflecting on life");
        String str1 = (String) f1.get(cc);
        System.out.println("field: " + str1);
    }

}

class Father implements Serializable {
    public String a_field;
}

class Child extends Father {
//empty class
}

请注意,我还将您的行String str1 = (String) f1.get(cc.getClass());更改为String str1 = (String) f1.get(cc);,因为您需要提供字段的对象,而不是类。

如果要将字段保密,则需要检索getter / setter方法并调用它们。你给出的代码不起作用,因为要获得一个方法,你还需要指定它的参数,所以

cc.getClass().getMethod("setA_field");

必须是

cc.getClass().getMethod("setA_field", String.class);

这是一个有效的代码:

public class Main {

    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("Child");
        Object cc = clazz.newInstance();
        cc.getClass().getMethod("setA_field", String.class).invoke(cc, "aaaaaaaaaaaaaa");
        String str1 = (String) cc.getClass().getMethod("getA_field").invoke(cc);
        System.out.println("field: " + str1);
    }

}

class Father implements Serializable {

    private String a_field;

    public String getA_field() {
        return a_field;
    }

    public void setA_field(String a_field) {
        this.a_field = a_field;
    }

}

class Child extends Father {
    //empty class
}