Java 反射的基本使用
Field
通过获取对应的对象的类声明的字段,可以动态地修改指定对象的一些属性。Field
十分有用, 其中在使用泛型时可以带来很大的便利。
BookChapter
类:
import lombok.AccessLevel;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
@Data
@NoArgsConstructor(access = AccessLevel.PUBLIC)
public class BookChapter {
public Long isbn = 1L;
private Integer chapterId;
private Short chapterKind;
private String chapterName;
}
使用的 JDK 版本(JDK 15):
- 通过对应的类来获取声明的字段属性。
/*
获取指定类的 class 对象, 在这里是 BookChapter 类
*/
Class<BookChapter> chapterClass = BookChapter.class;
/*
获取对应类的构造函数, 这要求指定的类有默认的构造函数。即不传入任何参数的构造函数。
使用 lombok 的 @NoArgsConstructor 注解可以有效地添加对应的无参构造函数。
当然, 也可以通过在 getConstructor() 中添加对应的构造函数的参数获取指定的构造函数
*/
Constructor<BookChapter> constructor = chapterClass.getConstructor();
/*
构造一个新的实体对象, 如果获取到的构造函数是带参数的, 那么在这里实例化对象时也必须添加对应的构造函数参数:new Object(){} 的形式
*/
BookChapter obj = constructor.newInstance();
/*
获取这个类声明的字段属性列表
*/
Field[] fields = chapterClass.getDeclaredFields();
- 设置对应的字段属性值
/*
遍历属性字段,设置对应的属性值
*/
for (Field field: fields) {
/*
设置该字段是可以访问的, 感觉很怪。。。。。
*/
field.setAccessible(true);
/*
对BookChapter对象字段设置对应的属性值, 由于只是学习, 因此只是使用几个简单的数据验证。
对于泛型的字段的属性值的设置, 可以将要设置的字段属性值转变为 Object 类型的对象,
由于 Object 是所有类的父类, 因此可以使用 Field的 set 方法设置属性值。
*/
switch (field.getName()) {
case "isbn" -> field.set(obj, 123456L);
case "chapterId" -> field.set(obj, 1);
case "chapterKind" -> field.set(obj, (short) 1);
case "chapterName" -> field.set(obj, "Hello World");
default -> System.out.println("Bad Object");
}
}
- 查看设置字段属性值后的 BookChapter 对象:
可以看到, 我们的 BookChapter 对象已经被设置了对应的属性值。 Field
的一些其他问题:
1)关于静态变量的属性值设置
事实证明, 使用Field
可以修改静态变量的属性值, 无论它是用private
、protected
、public
访问权限修饰的。但是当使用final
关键字修饰静态变量时, 尝试通过Field
修改属性值将会抛出IllegalAccessException
.
2)关于final
变量的属性值设置
对于仅仅是final
修饰的字段, 通过Field
是可以修改这个属性字段值的。
3)关于不同访问控制修饰的属性值的设置
使用不同的访问权限修饰的属性,使用Field
依旧是可以随意地修改对应的属性字段值。
因此, 在使用Field
时需要要特别注意, 不像通常使用的 Getter
和 Setter
方法, Field
的使用可能会破坏类的封装性。此外, 由于 final
的修饰对于 Field
的没有作用, 因此使用 final
关键字的属性在使用Field
时一定要将它的访问可访问字段设置为false
。