反射的概念
反射是JAVA程序开发的重要部分,最大的优势就是可以在程序运行期间对字节码文件进行修改,通过反射修改代码运行片段。
比如类中的private 属性、方法只能在内部访问,但是通过反射没有这样的限制。通过反射可以获取对象的属性、方法、构造函数等信息,通过对这些信息进行抽象成类进行操作。可以将一个对象分解一个个组成部分,可以任意的组合成新的对象。
字节码文件被加载的时候会生成一个Class对象,并且一个字节码文件(xxx.class)只会生成一个Class对象(可以称之为字节码对象)。反射就是通过Class对象获取属性、方法、构造函数等信息。
获取字节码对象(三种)
- 通过xxx.class获取
Class<User> clazz = User.class;
- 通过类的包路径获取
Class clazz = Class.forName("cn.unuuc.pojo.User");
- 通过对象实例获取
User user = new User();
Class clazz = user.getClass();
获取类中的属性
Field类是对象属性的抽象,通过字节码对象可以对属性进行操作。
常用方法如下:
- 获取字节码属性
// 只能获取public属性
Field[] fields1 = User.class.getFields();
// 获取所有属性,无论public、private
Field[] fields2 = User.class.getDeclaredFields();
// 获取指定属性名的Field对象
Field name = User.class.getDeclaredField("name");
一半一个类可能会存在集成的关系,如果想获取所有属性包括父类,代码如下:
Field[] fields = ArrayUtil.addAll(User.class.getDeclaredFields(),
User.class.getSuperclass().getDeclaredFields());
这里getSuperclass()方法表示获取父类Class对象,最后通过hutool工具提供数据工具类合并。
field设置值
public void set(Object obj, Object value);
参数一:需要设置的对象实例
参数二: 值
User user = new User();
field.set(user,"小红");
初始化的案例
一个类中有很多BigDecimal类型的属性,如果不对齐进行初始化则容易报空指针异常。一个一个set去初始化太过于麻烦,通过反射将所有BigDecimal类型的属性初始化。
代码如下
package cn.unuuc.pojo;
import cn.hutool.core.util.ArrayUtil;
import java.lang.reflect.Field;
import java.math.BigDecimal;
public class BigDecimalPojo {
private BigDecimal a1;
private BigDecimal a2;
private BigDecimal a3;
private BigDecimal a4;
private BigDecimal a5;
private BigDecimal a6;
private BigDecimal a7;
public BigDecimalPojo initZero(){
// 考虑到继承,合并数组
Field[] fields = ArrayUtil.addAll(this.getClass().getDeclaredFields(), this.getClass().getSuperclass().getDeclaredFields());
for (Field field : fields) {
if(field.getType().equals(BigDecimal.class)){
// 需要放开权限
field.setAccessible(true);
try {
if(field.get(this) == null){
field.set(this,BigDecimal.ZERO);
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
return this;
}
}
通过反射获取构造函数
1. 获取构造函数数组
- getConstructors();获取public的构造函数
- getDeclaredConstructors();获取所有构造函数,不考虑私有
public static void main(String[] args) {
Constructor<?>[] constructors = User.class.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println(constructor);
}
Constructor<?>[] declaredConstructors = User.class.getDeclaredConstructors();
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor);
}
}
2. 获取指定的构造方法
例如:User中有如下构造函数
@Data
public class User extends BaseModel{
public User(){
}
public User(String name,String address){
this.name = name;
this.address = address;
}
private String name;
private Integer age;
private Double height;
private String address;
}
现在想获取带参数的构造函数,代码如下:
public static void main(String[] args) throws NoSuchMethodException {
Constructor<User> declaredConstructor = User.class.getDeclaredConstructor(String.class, String.class);
System.out.println(declaredConstructor);
}
输出结果:
public cn.unuuc.pojo.User(java.lang.String,java.lang.String)
3.通过反射获取带参数构造函数实例化对象
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Constructor<User> declaredConstructor = User.class.getDeclaredConstructor(String.class, String.class);
User user = declaredConstructor.newInstance("张三", "南京市江宁区");
System.out.println(user);
}
- newInstance(Object … initargs); 参数是一个可变参数,Constructor类中的方法,用于实例化对象
输出结果:
User(name=张三, age=null, height=null, address=南京市江宁区)
获取类的方法
- getMethods();
- getDeclaredMethods();
public static void main(String[] args) {
// 获取public方法
Method[] methods = User.class.getMethods();
for (Method method : methods) {
System.out.println(method);
}
// 获取所有的方法
Method[] declaredMethods = User.class.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod);
}
}
获取指定方法并调用
例如User类中有如下方法:
public void eat(String name) {
System.out.println(name + "eat.........");
}
根据方法名获取方法对象:
因为方法可能会同重载的可能,即一个类中可能存在同名方法,参数不一样。所以需要通过方法参数类型区分。
关键代码:
User.class.getDeclaredMethod(“eat”, String.class);
Method eatMethod = User.class.getDeclaredMethod("eat", String.class);
User user = User.class.newInstance();
eatMethod.invoke(user,"张三");
通过调用Method中invoke() 方法来完成具体的调用
输出结果:
张三eat…
评论区