Java进阶-反射

189 阅读6分钟

反射的使用范围

能使用反射是程序处于运行期,如果编译器是获取不到的

反射的作用

反射可以在没有实例的情况下去获得,如在框架中并不能直接获取实例对象,还能突破权限限制 如private
但是反射只能访问对象的实例 并修改数据 调用方法等等 但是不能修改别人的代码 反射:就是在运行的时候才知道要操作的类是什么,并且可以在运行的时候获取完整的类的结构,并调用对应的方法

通过反射 我们能获取到类中的哪些信息呢

1.获取类的信息

String name = "包名.类名";(如类名是Person)
Class clazz = (Class)Class.forName(name);

2.获取类的构造函数

1.获取类的所有构造函数

Constructor[] constructors = (Constructor[]) clazz.getConstructor();

1.2获取指定的构造函数

Constructor constructor = (Constructor) clazz.getConstructor(参数类型1.class,参数类型2.class,...)(注意 如果是int,double 等基本参数类型 应该用int.class double.class);

1.3.通过构造函数new出对象

Person obj = constructor.newInstance(参数值1,参数值2,...);(如Person obj = constructor.newInstance("姓名陈",24));

2.获取类中的方法

2.1获取clazz对应类中的所有方法,且获取从父类继承来的所有方法,但不包括private方法

Method[] methods = clazz.getMethods();

2.2获取clazz对应类的所有方法,但只获取当前类的所有方法,也包括private方法>

Method[] methods = clazz.getDeclaredMethods();

2.3获取指定的方法,需要参数名称和参数列表,无参数则不需要写

Method method = clazz.getDeclaredMethod("setName",String.class);(第一个是方法名,第二个是参数类型)

2.4执行具体的方法,第一个参数表示执行哪个对象的方法,剩余的参数是执行方法时需要传入的参数

Person person = clazz.newInstance();
method.invoke(person,18);(第一个参数是对象,第二个参数是方法的参数值);
私有方法必须在调用invoke之前加上一句method.setAccessible(true);

3.获取类中的成员变量 但是不能获取局部变量

这个参考后面的代码例子(代码例子写的非常完整)

4.获得类中注解相关的方法

实例代码

//调用
 ReflectDemo.testClass("com.xm.studyproject.reflect.Person");
        ReflectDemo.testConstructor("com.xm.studyproject.reflect.Person");
        ReflectDemo.testMethod(Person.class);
        ReflectDemo.testField(Person.class);
 --------------------------------------
 //非常核心的代码 全是精华
 
 package com.xm.studyproject.reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * 主要是针对反射的工具类。
 * 反射使用的周期是在运行期 如果信息需要在编译期拿到是做不到的 可能需要注解处理器。
 * 反射思想:就是在运行的时候才需要操作什么,获取类的完整结构 比如构造函数 变量 方法等并调用
 * 什么时候需要使用反射呢?正常情况下我们可以new对象,但是有些框架的时候 我们没法直接使用
 * 反射对性能是有一定影响的 但是没有我们想象的那么夸张
 */
public class ReflectDemo {

    public static void testClass(String className) {
        try {
            //通过包名+类名的形式
            Class<?> aClass = Class.forName(className);
            //通过类实例化对象 如果没有非参的构造函数 需要传参 不然会报错
            //Class.newInstance() 只能够调用无参的构造函数,即默认的构造函数 如果该类没有默认的构造函数 直接这样调用会报错
            //如果想通过调用带参的生成带参的实例 需要通过newInstance Constructor.newInstance() 可以根据传入的参数,调用任意构造函数。
            //具体使用参考 https://blog.csdn.net/qq_41378597/article/details/102486128
            //会报错 下面这种写法会报错
           Person person = (Person) aClass.newInstance();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
    }

    //通过包名+类名的形式
    public static void testConstructor(String className) {
        Class<?> aClass = null;
        try {
            aClass = Class.forName(className);
            //获取当前类中的所有构造函数 包括private
            // private的范围是在当前类中
            // 默认修饰符的范围是当前包中的所有地方
            //protected的范围是当前包中所有地方 以及当前类的子类中
            Constructor<?>[] declaredConstructors = aClass.getDeclaredConstructors();
            for (Constructor<?> constructor : declaredConstructors) {
                System.out.println(constructor);
            }
            //获取当前类及父类的所有构造方法 但是不包括private
            Constructor<?>[] constructors = aClass.getConstructors();
            for (Constructor<?> constructor : constructors) {
                System.out.println(constructor);
            }
            //获取当前类(不包含父类)指定的构造函数 传入构造函数相应参数 但是如参数本身就是int 也是传入int而不是Integer
            //这种方式是获取不到私有的构造函数
            Constructor<?> constructor = aClass.getConstructor(String.class, int.class);
            //实例化构造函数 传入参数的真实值
            Person newInstance = (Person) constructor.newInstance("小米", 28);
            System.out.println(newInstance);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

    }

    //换种方式传入
    public static void testMethod(Class aClass) {
        //获取当前类中的所有方法 包括private方法
        Method[] declaredMethods = aClass.getDeclaredMethods();
        //获取当前类以及父类的所有方法 但是不包括private
        Method[] methods = aClass.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }
        //获取当前类(不包含父类)中指定的方法
        //第一个参数是方法名 也就是调用哪个方法 第二个参数就是方法中传入的参数
        Method method = null;
        try {
            //这种方式是获取不到私有方法的
            //method = aClass.getMethod("setInfo", String.class, int.class);
            //需要是这种方式
            method = aClass.getDeclaredMethod("setInfo", String.class, int.class);
            //调用方法前 如果是私有方法 需要设置可访问权限为true 不然会报错
            method.setAccessible(true);
            //调用方法
            //第一个参数是对象 也就是实例 因为没有实例就不存在调用方法一说
            //第二个就是参数赋值 也就是实际的值
            Constructor constructor = aClass.getDeclaredConstructor(String.class);
            Object o = constructor.newInstance("小小");
            System.out.println(method);
            Object invoke = method.invoke(o, "小花", 30);
            System.out.println(invoke);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }  catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
    }

    public static void testField(Class aClass) {
        //获取当前类的成员变量 不包括局部变量
        Field[] declaredFields = aClass.getDeclaredFields();
        for (Field field : declaredFields) {
            System.out.println(field);
        }
        //获取当前类及父类的成员变量 但是不包括private Object成员变量都是私有的
        Field[] fields = aClass.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }
        //获取指定变量
        //参数就是变量名
        Field field = null;
        try {
            //获取实例
            Constructor constructor = aClass.getDeclaredConstructor(String.class);
            Object object = constructor.newInstance("小小");

            //获取私有成员变量 如果没有获取到是会报错的
            //field = aClass.getField("name");
            //获取实例对象中变量的值
            field = aClass.getDeclaredField("name");
            //需要上面getDeclaredField可以获取到私有成员变量 但是如果想获取私有变量的值还是需要
            field.setAccessible(true);

            String name = (String) field.get(object);
            System.out.println(name);
            //对实例中的变量进行赋值 如果该变量是私有的需要设置
            field.setAccessible(true);
            field.set(object, "小陈同学");
            System.out.println(field.get(object));
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

    }
}


-----------------------------
//提供的实体bean
package com.xm.studyproject.reflect;

public class Person {
    private String name;
    private int age;

    private Person(String name) {
        this.name = name;
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    private void setInfo(String name, int age) {
        int tempName = 3;
        this.name = name;
        this.age = age;
    }
}


也可以参考(www.jianshu.com/p/9be58ee20…)