什么是“反射”和“内省”?

反射和内省

Property属性、Method方法、Field字段

反射

反射操作构造方法

    /**
     * 反射操作构造方法
     */
    @Test
    public void testReflectConstruct() throws Exception {
        // 1、创建反射对象
        Class<User> userClass = User.class;
        // 2、获得参数为String和Integer的公有构造器
        Constructor<User> constructor = userClass.getConstructor(String.class, int.class);
        // 3、通过获得的构造器创建对象和为参数赋值
        User user1 = constructor.newInstance("王政为2", 33);
        System.out.println("user1 = " + user1);

        /

        // 1、获取私有无参构造器
        Constructor<User> declaredConstructor = userClass.getDeclaredConstructor();
        // 2、允许访问(即无视private)
        declaredConstructor.setAccessible(true);
        // 3、通过获取到的无参构造器创建对象
        User user2 = declaredConstructor.newInstance();
        System.out.println("user2 = " + user2);
    }

通过反射操作无参、有参、私有、公有的方法

/**
 * 通过反射操作无参、有参、私有、公有的方法
 */
@Test
public void testMethod() throws Exception {
    // 1、创建反射对象
    Class<User> userClass = User.class;
    // 2、获得参数为String和Integer的公有构造器
    Constructor<User> constructor = userClass.getConstructor(String.class, int.class);
    // 3、通过获得的构造器创建对象和为参数赋值
    User user = constructor.newInstance("王政为2", 33);

    ///操作有参方法///
    
    // 1、通过反射的Class对象userClass获取到参数为String的方法eat
    Method method = userClass.getMethod("eat", String.class);
    // 2、指定调用对象并传入参数,调用方法
    method.invoke(user, "麻瓜");

    ///操作无参方法 & 操作私有方法///
    // 1、通过反射对象获取到无参的方法say
    Method declaredMethod = userClass.getDeclaredMethod("say");
    // 2、因为say是私有的,将其设置为允许访问(即无视private)
    declaredMethod.setAccessible(true);
    // 3、调用方法
    declaredMethod.invoke(user);

    ///获得所有方法///
    // 获得所有属于当前类的公有私有方法
    Method[] declaredMethods = userClass.getDeclaredMethods();
    for (Method dm : declaredMethods) {
        System.out.println("dm.getName() = " + dm.getName());
    }
    // 获得当前类及其父类的所有的公有方法
    Method[] methods = userClass.getMethods();
    for (Method m : methods) {
        System.out.println("m.getName() = " + m.getName());
    }
}

反射操作字段

    /**
     * 反射操作字段
     */
    @Test
public void testField() throws Exception {
        // 获取User.class对象
        Class<User> userClass = User.class;
        // 获得公有的参数依次为String和int的
        Constructor<User> constructor = userClass.getConstructor(String.class, int.class);
        // 获得实例对象
        User user = constructor.newInstance("王政象信息",22);
        System.out.println(user);

        获得字段
        // 1、获取私有字段name
        Field field = userClass.getDeclaredField("name");
        // 2、将私有设置为允许访问
        field.setAccessible(true);
        // 3、获取user中的name值,并打印
        System.out.println("field.get(user) = " + field.get(user));

        修改字段
        // 1、获取私有字段name
        Field name = userClass.getDeclaredField("name");
        // 2、将私有设置为允许访问
        name.setAccessible(true);
        // 3、修改user对象中的name值
        name.set(user,"象信息王政");
        // 打印
        System.out.println("field.get(user) = " + field.get(user));
    }
}

记忆要点

  1. 获取当前类的所有方法、属性、构造器(包括私有),一般是getDeclaredXXX(),不加Declared一般就是获取当前类及其父类的所有公有方法。
  2. 一般需要构建某个东西,如:构造器、JavaBean描述对象BeanInfo等是传入.class或者getClass()对象,因为其是独一无二的。
  3. 一般需要指定获得某个字段或者方法,一般需要指定或数据来自哪个对象,此时就不再是class对象,而是实例化对象。

内省

实体类

@Data
public class User {

    private String name = "王政";
    private int age = 21;

    private void say() {
        System.out.println("哈哈哈");
    }

    public void eat(String food) {
        System.out.println(food);
    }

    public User() {
    }

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

Map和JavaBean之间的相互转化

public class BeanUtils<T> {

    /**
     * 将JavaBean对象转换为Map
     */
    public static Map<String, Object> getMapByJavabean(Object bean) throws IntrospectionException, InvocationTargetException, IllegalAccessException {
        Map<String, Object> maps = new HashMap<>();
        // Object.class 是指断点,Object.class中的所有方法都被忽略
        BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass(), Object.class);
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
        for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
            String name = propertyDescriptor.getName();
            Object value = propertyDescriptor.getReadMethod().invoke(bean);
            maps.put(name, value);
        }
        return maps;
    }

    /**
     * 将Map转换为JavaBean对象
     */
    public static<T> T getJavaBeanByMap(Map<String, Object> maps ,Class<?> clazz) throws IntrospectionException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        BeanInfo beanInfo = Introspector.getBeanInfo(clazz,Object.class);
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
        Constructor<?> constructor = clazz.getConstructor();
        Object o = constructor.newInstance();
        T jBean = (T) o;
        for (PropertyDescriptor pd : propertyDescriptors) {
            String name = pd.getName();
            pd.getWriteMethod().invoke(jBean,maps.get(name));
        }
        return jBean;
    }

}
class Test{
    
    /**
     * 将JavaBean对象转换为Map集合
     */
    @Test
    public void testGetMapByJavaBean() throws IllegalAccessException, IntrospectionException, InvocationTargetException {
        Map<String, Object> maps = BeanUtils.getMapByJavabean(new User("王政", 22));
        System.out.println("maps.toString() = " + maps.toString());
    }

    /**
     * 将Map集合转换为JavaBean对象
     */
    @Test
    public void testGetJavaBeanByMap() throws InvocationTargetException, NoSuchMethodException, IntrospectionException, InstantiationException, IllegalAccessException {
        Map<String,Object> maps = new HashMap<>();
        maps.put("name","王政哈哈");
        maps.put("age",22);
        User javaBeanByMap = new BeanUtils<User>().getJavaBeanByMap(maps, User.class);
        System.out.println("javaBeanByMap = " + javaBeanByMap);
    }
}

通过内省操作JavaBean内容

public class UserTest {

    /**
     * 内省获取JavaBean对象中的方法
     */
    @Test
    public void testIntroMethod() throws Exception {
        User user = User.class.newInstance();
        // 1、通过字节码对象,获取到JavaBean的描述对象。
        BeanInfo beanInfo = Introspector.getBeanInfo(User.class);
        // 2、通过JavaBean描述对象获取 方法描述器,返回方法描述器数组
        MethodDescriptor[] md = beanInfo.getMethodDescriptors();
        // 3、遍历方法描述器数组
        for (MethodDescriptor methodDescriptor : md) {
            // 打印方法名
            System.out.println("methodDescriptor.getName() = " + methodDescriptor.getName());
        }
    }

    /**
     * 内省获取JavaBean对象中的属性
     */
    @Test
    public void testIntroPro2() throws Exception {
        // 通过反射获取到User对象
        // 1、获取类对象
        Class<User> userClass = User.class;
        // 2、获取类中构造器
        Constructor<User> constructor = userClass.getConstructor();
        // 3、通过构造器创建User对象
        User user = constructor.newInstance();
        // 4、通过User.class对象获取到JavaBean描述对象BeanInfo
        BeanInfo beanInfo = Introspector.getBeanInfo(User.class);
        // 5、通过JavaBean描述对象获取到 属性描述器,返回属性描述器数组。
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
        // 6、遍历属性描述器数组中的元素,获取单独的属性并操作
        for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
            if ("name".equals(propertyDescriptor.getName())) {
                // 获取name的设置器(Setter)并调用(修改的对象,数据)
                propertyDescriptor.getWriteMethod().invoke(user, "王政");
            } else if ("age".equals(propertyDescriptor.getName())) {
                // 获取age的设置器(Setter)并调用(修改的对象,数据)
                propertyDescriptor.getWriteMethod().invoke(user, 11);
            }
            // 获取每个属性描述器数组元素的获得器(Getter),并调用
            System.out.println("propertyDescriptor.getReadMethod() = " + propertyDescriptor.getReadMethod().invoke(user));
        }
    }
}

内省的定义就是通过反射和Introspector类获取到JavaBean的描述对象BeanInfo,通过描述对象获得传入的Class类对象类中满足JavaBean规范的属性和方法

反射和内省的区别

反射:反射操作任何类中的所有成员。

内省:内省只能操作符合JavaBean规则的类的成员。

使用反射打破单例

/**
 * @projectName: 10_reflect
 * @package: cn.wolfcode.test_single
 * @className: SayUtilsSingle
 * @author: 王政
 * @description: TODO 单例类
 * @date: 21/7/27 16:06
 * @version: 1.0
 */
public class SayUtilsSingle {
    private static final SayUtilsSingle SAY_UTILS_SINGLE = new SayUtilsSingle();

    private static int num = 1;

    public static SayUtilsSingle getInstance(){
        return SAY_UTILS_SINGLE;
    }

    public void say(){
        System.out.println("哈哈哈哈" + num);
    }

    private SayUtilsSingle() {
        num++;
    }
}

通过反射打破单例

/**
 * @projectName: 10_reflect
 * @package: cn.wolfcode.test_single
 * @className: BrokenSingle
 * @author: 王政
 * @description: TODO
 * @date: 21/7/27 16:10
 * @version: 1.0
 */
public class BrokenSingle {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        // 正常操作
        SayUtilsSingle single = SayUtilsSingle.getInstance();
        single.say();
        // 打破
        // 获取单例类的类对象
        Class<SayUtilsSingle> susc = SayUtilsSingle.class;
        // 获取类对象的无参构造方法(不管是否是private修饰都能获取到)
        Constructor<SayUtilsSingle> declaredConstructor = susc.getDeclaredConstructor();
        // 允许访问私有
        declaredConstructor.setAccessible(true);
        // 通过构造器创建对象,成功打破单例
        SayUtilsSingle single1 = declaredConstructor.newInstance();
        single1.say();
    }
}

建议:需要使用单例模式的时候通过枚举类创建单例,枚举的单例是无法被反射打破的。

注意事项

  1. 在使用反射时候,我们通过.class或者getClass()获取到的反射对象,通过反射对象调用getDeclaredFields()方法获取到的包含本类所有的字段的数组,其中的元素是按照类中书写的顺序存储的。
  2. 在使用内省时候,我们通过Introspector对象调用getPropertyDescriptors()方法获取到的包含本类所有的字段的数组,其中的元素是按照元素名称的顺序从小到大排序存储的。

**重要:**在传输基本数据的时候以Object类型接收,此时使用接收来的数据获取到的类对象是基本数据类型的包装类,解决方法就是尽量在传输数据和设计实体类的时候变量均设置为包装数据类型。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

君去何方

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值