Java 的反射、内省,你会用吗?

开发 前端
反射是Java语言的一个特性,允许程序在运行时检查和操作类、方法、字段等。反射可以动态地获取类的所有属性和方法,并且可以动态调用这些方法。

你好,我是看山。

Java的反射和内省是两个在运行时操作类和对象的强大机制,它们之间存在关联和区别。很多时候我们用错了,就会有性能上的损失。

概念

反射(Reflection)

反射是Java语言的一个特性,允许程序在运行时检查和操作类、方法、字段等。反射可以动态地获取类的所有属性和方法,并且可以动态调用这些方法。

反射强调的是运行状态,即在程序运行时能够访问和修改类的状态或行为。反射提供了更底层的类结构和行为访问机制。

反射的核心类都在java.reflect包下,主要类如下:

图片图片

图片图片

内省(Introspection)

内省是基于反射实现的,主要用于操作符合JavaBean规范的类。

JavaBean是一种特殊的Java类,通常用于封装多个属性为一个单一的对象。

内省机制通过反射获取属性描述器(PropertyDescriptor),然后可以方便地获取和设置属性值。

内省操作只针对JavaBean,只有符合JavaBean规则的类的成员才可以采用内省API进行操作。

内省的核心类在java.beans包下,主要类如下:

图片图片

关系与区别

  • 适用范围:

反射可以操作任何类的所有成员,包括私有成员。

内省主要针对JavaBean,只能操作符合JavaBean规范的类的成员。

  • 操作方式:
  • 反射是先得到类的字节码(Class)后再进行各种操作。
  • 内省是先得到属性描述器(PropertyDescriptor)后再进行各种操作。
  • 复杂度:
  • 反射提供了更底层的访问机制,使用起来相对复杂。
  • 内省提供了一套比较简单和有层次的API,更专注于JavaBean的属性操作。
  • 应用场景:
  • 反射适用于需要动态获取和调用类信息的场景,如框架开发。
  • 内省适用于需要操作JavaBean属性的场景。

来点小栗子

使用反射创建对象并调用方法

我们通过反射创建对象并调用指定方法:

public static void main(String[] args)
        throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException,
        IllegalAccessException, NoSuchFieldException {
    final Class<?> clazz = MyClass.class;
    // Class<?> clazz = new MyClass().getClass();
    // Class<?> clazz = Class.forName("cn.howardliu.tutorials.core.reflect.ReflectDemo.MyClass");

    final Constructor<?> constructor = clazz.getConstructor();
    // Constructor<?> constructor = clazz.getConstructor(String.class);
    final Object o = constructor.newInstance();

    final Field field = clazz.getDeclaredField("name");
    field.setAccessible(true);
    field.set(o, "看山");

    final Method method = clazz.getMethod("echoName");
    final Object result = method.invoke(o);
    System.out.println(result);
}

@Data
@NoArgsConstructor
@AllArgsConstructor
publicstaticclass MyClass {
    private String name;

    public String echoName() {
        return name;
    }
}

首先,我们获取class对象,有三种方式:

  • 通过类名直接获取:Class<?> clazz = MyClass.class;
  • 通过实例获取:Class<?> clazz = new MyClass().getClass();
  • 通过加载类:Class<?> clazz = Class.forName("cn.howardliu.tutorials.core.reflect.ReflectDemo.MyClass"); 。

然后获取构造器对象,使用Class对象的getConstructor()或getDeclaredConstructor()方法获取构造器对象。

  • 无参构造函数:Constructor<?> constructor = clazz.getConstructor();
  • 有参构造函数:Constructor<?> constructor = clazz.getConstructor(String.class); 。

使用newInstance()创建实体对象:final Object o = constructor.newInstance();。

示例中我们对name属性赋值,首先得获取Field对象,可以用getDeclaredField()或者getField()。

因为name属性是非public的,调用setAccessible设置可访问,然后赋值。

最后就是获取Method对象,使用invoke方法实现对象的动作。

以上就是反射常用的逻辑了。反射还提供了基于枚举的API,有需要的时候可以用一用。

通过内省给对象赋值

内省就是转为JavaBean准备的,主要包括下面几个类:

  1. Introspector类:这是内省API的核心类,提供了获取BeanInfo对象的方法,例如Introspector.getBeanInfo()方法。
  2. BeanInfo类:这个类包含了关于一个对象的所有Bean属性信息,包括属性的描述符(PropertyDescriptor)。
  3. PropertyDescriptor类:表示一个JavaBean属性的信息,包括getter和setter方法。

以下是一个简单的示例,我们首先获取User的JavaBean信息BeanInfo,然后找到属性描述符PropertyDescriptor列表。

使用属性的读方法获取数据,使用写方法赋值。

public static void main(String[] args)
        throws IntrospectionException, InvocationTargetException, IllegalAccessException {
    final BeanInfo beanInfo = Introspector.getBeanInfo(User.class);
    final User user = new User();

    // 遍历所有属性描述符
    for (PropertyDescriptor prop : beanInfo.getPropertyDescriptors()) {
        System.out.println("Property Name: " + prop.getName());

        // 获取getter方法
        final Method readMethod = prop.getReadMethod();
        if (readMethod != null) {
            Object value = readMethod.invoke(user);
            System.out.println("Property Value: " + value);
        }

        if ("username".equals(prop.getName())) {
            // 获取setter方法
            final Method writeMethod = prop.getWriteMethod();
            if (writeMethod != null) {
                Object value = writeMethod.invoke(user, "看山");
                System.out.println("Property Value: " + value);
            }
        }
    }
    System.out.println(user);
}

@Data
publicstaticclass User {
    private String username;
}

在JavaBean的操作方面,内省确实比反射更方便。

文末总结

今天通过示例介绍了Java的反射和内省,下次我们看看在Bean赋值方面,两者的性能差异有多少。

责任编辑:武晓燕 来源: 看山的小屋
相关推荐

2025-01-10 00:00:00

内省机制JavaBean描述器

2010-03-12 08:55:06

Java内省反射

2020-06-04 14:15:55

Java中BigDecimal函数

2018-09-29 15:34:34

JavaList接口

2025-01-15 00:00:00

Java内省性能

2019-07-25 12:46:32

Java高并发编程语言

2023-11-01 13:48:00

反射java

2021-05-21 12:36:16

限流代码Java

2024-03-06 08:15:03

@Autowired注入方式Spring

2023-12-01 11:13:50

JavaTreeSet

2021-09-06 10:42:18

Linux命令服务器

2021-08-11 10:00:51

缓存MyBatis管理

2019-01-28 17:42:33

Python数据预处理数据标准化

2025-01-03 08:40:53

Java并发编程Guava库

2021-11-03 17:40:51

Python线程

2009-07-14 18:09:08

Jython的内省

2022-02-10 09:04:50

架构

2024-04-08 00:00:00

asyncawaiPromise

2022-11-07 17:50:36

2015-09-06 10:14:21

swift加载动画实例教程
点赞
收藏

51CTO技术栈公众号