使用反射设置私有字段的值

120

我有两个类:FatherChild

public class Father implements Serializable, JSONInterface {

    private String a_field;

    //setter and getter here

}

public class Child extends Father {
    //empty class
}

使用反射,我想在Child类中设置a_field

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

但我有一个异常:

线程“main”中的异常:java.lang.NoSuchFieldException:a_field

但如果我尝试:

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

它起作用了。

使用设置器方法,我遇到了相同的问题:

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

3
在能够访问字段之前,您需要首先获取类的超类。另一个SO问题 - SomeJavaGuy
1
该字段不是Child类的一部分。由于它是私有的,因此在Child类中甚至都不可见。Setter方法是公共的并且被继承了,这意味着它们可以像在Child类中声明的一样被调用。 - f1sh
不要直接获取私有字段,而是通过获取其setter方法来设置其值。 - Manas Pratim Chamuah
是的,这是一个好的解决方案。现在我可以尝试。 - user1066183
5个回答

183

要访问私有字段,您需要将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);

138

使用来自Apache Commons Lang 3FieldUtils

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

true 强制设置,即使字段是 私有的


4
感谢这个逃生口,让我们摆脱了那些必须通过Spring Bean依赖注入设置的愚蠢领域。 - Sridhar Sarnobat
尝试在RuntimeException的字段“cause”上执行它-它不起作用。适用于Jdk11+。 - pegoopik
https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 - Dave Ankin

19

Kotlin版本

使用以下扩展函数获取私有变量

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

使用set和get方法设置和获取变量:

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");

1
非常好的回答。总体而言,没有依赖性,我喜欢它。 - Michael Piefel

7
这个可以访问私有字段,而不需要做任何事情。
import org.apache.commons.lang3.reflect.FieldUtils;
Object value = FieldUtils.readField(entity, fieldName, true);

-5
根据 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
}

谢谢.. 但 a_field 必须是私有的。另外我可以调用 setter 方法吗? - user1066183

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接