Java反射机制初探

这是我参与11月更文挑战的第5天,活动详情查看:2021最后一次更文挑战

前言

反射机制在Java内容中十分重要,也是Java被称为准动态语言的一部分原因。

动态语言:是在运行时确定数据类型的语言。变量使用之前不需要类型声明,通常变量的类型是被赋值的那个值的类型。

静态语言:是在编译时确定变量的数据类型的语言,多数静态类型语言要求在使用变量之前必须声明数据类型。

什么是反射?

能够分析类能力的程序称之为反射--《Java核心技术》

简单来说,在执行完类加载后,在堆里会出现一个存放该类信息的一个Class对象,通过这个对象我们可以得到

加载的类的一些结构信息,就好像镜子里的自己,这种现象就被称为反射

image-20211105082358455

反射机制的作用

  1. 在运行时分析类的能力
  2. 在运行时检查对象
  3. 实现泛型数组操作代码
  4. 利用Method等对象

咱们一个一个来说

1. 利用反射分析类的能力

​ 在java.lang.reflect包内存在三个Field、Method、Constructor类,分别用来描述类的成员变量,方法以及构造器

而在之前我们也看到过这些类会出现在类加载中出现的Class类对象中

image-20211105082358455

所以提供Class类对象,我们可以了解到很多很多对应类的信息。

@SuppressWarnings({"all"})
public class Test01 {
    public static void main(String[] args) throws NoSuchMethodException,NoSuchFieldException {
        Cat cat = new Cat();
        Class aClass = cat.getClass();

        // 得到aClass对象中对应Cat类中方法的方法对象数组
        Method[] methods = aClass.getMethods();
        // 提供方法名去得到对应的方法对象
        Method method = aClass.getMethod("CarName");

        // 得到aClass对象中对应Cat类中字段的字段对象数组
        Field[] fields = aClass.getFields();
        // 提供字段名去得到对应的字段对象
        Field age = aClass.getField("age");

        // 得到aClass对象中对应Cat类中构造器的构造器对象数组
        Constructor[] constructors = aClass.getConstructors();
        // 要得到指定的构造器就比较特殊,需要传入对应对象的类的Class对象
        Constructor constructor = aClass.getConstructor(String.class, int.class);
        /**
         * public Cat(String name, int age) {
         *         this.name = name;
         *         this.age = age;
         *     }
         */
    }
}
复制代码

2、利用反射在运行时检查对象

同样也是去利用Class对象,在运行时得到有关类的信息,并且进行一定的操作(比如说:创建一个针对所有类的一个toString方法)

 
   public class test1 {

    private String name = "test";
    private int age = 12;
    private String home = "home";
    
    
    public void mms (){
        System.out.println("Mod");
    }
    public String toString()
    {
        Field[] fields=this.getClass().getDeclaredFields();
        StringBuffer strBuf=new StringBuffer();
        strBuf.append(this.getClass().getName());
        strBuf.append("[");
        for(int i=0;i<fields.length;i++)
        {
            Field fd=fields[i];
            strBuf.append(fd.getName()+":");
            try
            {
                strBuf.append(fd.get(this));
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
            if(i!=fields.length-1)
                strBuf.append("|");
        }

        strBuf.append("]");
        return strBuf.toString();
    }
}

复制代码

3、利用反射机制实现泛型数组操作代码

Java.reflect.Array类允许动态的创建数组,所以可以进行一些相关的操作,比如使用Array.copyOf方法实现数组扩容

public class test2 {
    private static int[] tmp = {1,2};
    public static int[] add(int[] num, int newLength){
        // 拿到数组的Class类对象
        Class<? extends int[]> aClass = tmp.getClass();
        // 判断是不是数组类
        if(!aClass.isArray()){
            System.out.println("不是数组");
            return null;
        }
        else {
            // 表示数组类的对象定义了这个类,确定数组的正确类型
            Class<?> componentType = aClass.getComponentType();
            int length = Array.getLength(tmp);
            // 创建新的数组
            Object o = Array.newInstance(componentType, newLength);
            return (int[]) o;
        }

    }
    public static void main(String[] args) {
        System.out.println(tmp.length);
        int[] add = add(tmp, 5);
        System.out.println(add.length);
    }
}
复制代码

4.利用Method等对象,进行调用方法

反射机制可以让你通过获得Class对象中的Method对象来调用对应方法

之前调用是 对象.方法(如:cat.CarName())

通过Method方法调用是 Method对象.invoke(对象)(如:carName.invoke(o))

public class Test4 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
        // 通过反射动态加载类
        Class aClass = Class.forName("reflex.testclass.Cat");

        // 通过方法名获得指定方法对象
        Method carName = aClass.getMethod("CarName");

        // 通过newInstance创建实例对象(使用无参构造器)
        Object o = aClass.newInstance();

        // 通过Method方法调用 执行方法
        String invoke = (String) carName.invoke(o);

        // 输出返回值
        System.out.println(invoke);
    }
}
复制代码