侧边栏壁纸
博主头像
实习两年半

基础不牢,地动山摇。

  • 累计撰写 44 篇文章
  • 累计创建 40 个标签
  • 累计收到 4 条评论

目 录CONTENT

文章目录

JAVA反射走一波

实习两年半
2022-11-05 / 0 评论 / 0 点赞 / 813 阅读 / 1,199 字
温馨提示:
本文最后更新于 2022-11-06,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

反射的概念

     反射是JAVA程序开发的重要部分,最大的优势就是可以在程序运行期间对字节码文件进行修改,通过反射修改代码运行片段。
     比如类中的private 属性、方法只能在内部访问,但是通过反射没有这样的限制。通过反射可以获取对象的属性、方法、构造函数等信息,通过对这些信息进行抽象成类进行操作。可以将一个对象分解一个个组成部分,可以任意的组合成新的对象。

字节码文件被加载的时候会生成一个Class对象,并且一个字节码文件(xxx.class)只会生成一个Class对象(可以称之为字节码对象)。反射就是通过Class对象获取属性、方法、构造函数等信息。

获取字节码对象(三种)

  1. 通过xxx.class获取
Class<User> clazz = User.class;
  1. 通过类的包路径获取
Class clazz = Class.forName("cn.unuuc.pojo.User");
  1. 通过对象实例获取
User user = new User();
Class clazz = user.getClass();

获取类中的属性

    Field类是对象属性的抽象,通过字节码对象可以对属性进行操作。

常用方法如下:

  1. 获取字节码属性
// 只能获取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…

0

评论区