反射 - - - - - - - - 内省【精细】

1、概念

Java内省机制(Introspection)是一种访问Java Bean属性的方式,它允许程序在运行时获取和设置Java对象的属性。通过内省机制,程序可以在运行时获取对象的属性名、类型和值,并动态地设置对象的属性值。内省机制为Java反射机制的一个重要应用,使得Java Bean对象可以更加灵活地被访问和处理。

2、Java内省的作用

  1. 动态访问Java对象的属性和方法:Java内省可以在运行时动态获取Java对象的属性和方法信息,并访问其属性值和调用其方法。
  2. 避免硬编码对象的属性和方法名称:Java内省可以通过反射机制避免硬编码对象的属性和方法名称,从而使程序更加灵活和可扩展。
  3. 提高代码的可读性和可维护性:Java内省可以使代码更加简洁和易读,同时也方便了代码的维护和修改。
  4. 支持动态代理和AOP编程:Java内省可以支持动态代理和AOP编程,从而实现更加灵活和可扩展的编程。

3、Java内省的实现机制

Java内省的实现机制基于Java的反射机制。在Java中,每个类都有一个Class对象,可以通过这个Class对象获取类的各种信息,包括类的名称、构造方法、属性和方法等。Java内省通过分析Class对象来获取Java对象的属性和方法信息,并通过反射机制访问其属性值和调用其方法。

二、Java内省的核心类

Java内省的核心类包括 IntrospectorBeanInfoPropertyDescriptorMethodDescriptor 和IntrospectionException

1、Introspector类

Introspector 是一个工具类,它提供了一种标准方法来了解目标 Java Bean 支持的属性、事件和方法。通过调用Introspector.getBeanInfo()方法,可以获取封装 JavaBean 相关信息的 BeanInfo 对象,如下所示:

(1)static BeanInfo getBeanInfo(Class<?> beanClass)

获取beanClass对应的 Javabean 对象的 BeanInfo 的封装

(2)static BeanInfo getBeanInfo(Class<?> beanClass, Class<?> stopClass)

获取 beanClass 对应的 Javabean 对象的 BeanInfo 的封装。其中,stopClass 指定停止分析(解析)的基类。分析中将忽略 stopClass 或其基类中的任何方法、属性、事件。

2、BeanInfo接口

BeanInfo 对象是 Java 内省的核心对象之一,用于描述JavaBean的所有属性和方法信息。它包含了一个或多个PropertyDescriptor对象和MethodDescriptor对象,用于描述JavaBean的所有属性和方法信息。BeanInfo接口提供了一组用于访问JavaBean的属性和方法信息的方法,如下所示:

getPropertyDescriptors()

返回一个PropertyDescriptor数组,用于描述JavaBean的所有属性信息。每个PropertyDescriptor对象包含了一个属性的名称、类型和读写方法等信息。

getMethodDescriptors()

返回一个MethodDescriptor数组,用于描述JavaBean的所有方法信息。每个MethodDescriptor对象包含了一个方法的名称、参数类型和返回值类型等信息。

3、PropertyDescriptor类

注意:内省是根据标准的 setter 或 getter 方法来获取属性信息。

如果在类中,定义了属性,但并没有提供标准的 setter 或 getter 方法,则读取不到任何属性信息;反之,如果在类中,没有定义属性,但提供了标准的 setter 或 getter 方法,则可以读取对应的属性信息。例如:Object 类通过 getter 方法:getClass(),可以读取到 class 属性。

PropertyDescriptor类是用于描述 JavaBean 的一个属性信息。它包含了一个属性的名称、类型和读写方法等信息。PropertyDescriptor 类提供了一组用于访问 JavaBean 的属性信息的方法,如下所示:

  • getName():方法返回属性的名称。

  • getPropertyType():返回属性的类型。

    • isPrimitive():判断数据类型是否为基本数据类型
  • getReadMethod():返回属性的读方法,即getter方法。

  • getWriteMethod():返回属性的写方法,即setter方法。

4、MethodDescriptor类

MethodDescriptor类是用于描述JavaBean的一个方法信息。它包含了一个方法的名称、参数类型和返回值类型等信息。MethodDescriptor类提供了一组用于访问JavaBean的方法信息的方法,如下所示:

  • getName():返回方法的名称。
  • getParameterTypes():返回方法的参数类型数组。
  • getReturnType():返回方法的返回值类型。

调用 getMethod() 方法可以获得指定方法的 Method 类型对象,然后就可以利用反射执行指定的方法。

5、IntrospectionException类

IntrospectionException类是 Java 内省过程中可能会抛出的异常类。它表示 Java 内省过程中发生的异常情况,例如 JavaBean 的属性或方法不存在、不合法的属性或方法等。

三、Java内省的基本用法

下面是一个简单的示例代码,演示如何使用 Java 内省获取 JavaBean 的属性和方法信息,并访问和设置属性值。内省的具体操作步骤如下:

在 Java 内省中,一个 JavaBean 是一个符合特定规范的 Java 对象,该规范要求该对象具有无参构造函数以及一组可读可写的属性。JavaBean 的属性通常通过 get 和 set 方法进行访问。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
 * 第一:定义 JavaBean 对象
 *
 * @author zqx
 * @date 2023-03-04
 */
public class Person {
  private String name;
  private int age;

  // setter/getter
}

/**
 * JavaBean 继承
 *
 * @author zqx
 * @date 2023-03-04
 */
public class Staff extends Person {
  private String department;
  private double salary;
  
  // setter/getter
}

1、获取 JavaBean 的 BeanInfo 对象

BeanInfo 对象表示的是 JavaBean 的信息,可以通过 Introspector 类调用相关方法获得的。

1
2
3
4
5
BeanInfo beanInfo = Introspector.getBeanInfo(类.class);
BeanInfo beanInfo = Introspector.getBeanInfo(子类.class,父类.class);

BeanInfo beanInfo = Introspector.getBeanInfo(Person.class);
BeanInfo beanInfo = Introspector.getBeanInfo(Staff.class,Person.class);

2、获取 JavaBean 的属性和方法信息

可以通过 BeanInfo 类的 getPropertyDescriptors() 方法和 getMethodDescriptors() 方法获取 JavaBean 的所有属性和方法信息,然后遍历属性和方法描述符来获取具体的属性和方法信息。

1)获取属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 1.获取属性描述器
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();

// 2.遍历属性描述器
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
  // 获取属性名称
  String propertyName = propertyDescriptor.getName();

  // 获取属性的数据类型
  Class<?> propertyType = propertyDescriptor.getPropertyType();

  System.out.println("属性的修饰符:" + propertyType.getModifiers());
  System.out.println("属性的修饰符:" + Modifier.toString(propertyType.getModifiers()));
  System.out.println("属性的数据类型:" + propertyType.getName());
  System.out.println("属性的数据类型:" + propertyType.getSimpleName());
  System.out.println("属性的数据类型是否为基本类型:"+propertyType.isPrimitive());
  System.out.println("属性名称:" + propertyName);
  System.out.println("----------------");
}
2)获取方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 1.获取方法描述器
MethodDescriptor[] methodDescriptors = beanInfo.getMethodDescriptors();

// 2.遍历方法描述器
for (MethodDescriptor methodDescriptor : methodDescriptors) {
  // 输出方法名称、参数类型和返回值类型
  int modifiers = methodDescriptor.getMethod().getModifiers();
  System.out.println("方法修饰符:" + Modifier.toString(modifiers));
  System.out.println("方法返回值:" + methodDescriptor.getMethod().getReturnType().getName());
  System.out.println("方法名称:" + methodDescriptor.getName());
  System.out.println("方法名称:" + methodDescriptor.getMethod().getName());
  System.out.println("方法参数:");
  Class<?>[] parameterTypes = methodDescriptor.getMethod().getParameterTypes();

  for (Class<?> parameterType : parameterTypes) {
    System.out.println("\t参数类型:" + parameterType.getName());
    System.out.println("\t参数类型:" + parameterType.getSimpleName());
  }

  System.out.println("----------------");
}

3、访问 JavaBean 的属性值

可以通过 PropertyDescriptor 类的 getReadMethod() 方法获取 JavaBean 的 getter 方法,然后通过反射机制调用getter方法获取属性值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 实例化 Person 对象 - JavaBean 对象
Person bean = new Person("zing", 18);

// 循环遍历 Person 对象的所有属性
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
  // 获取 Person 对象的属性名称
  String propertyName = propertyDescriptor.getName();

  // 获取Person对象的写方法 -- getter方法
  Method readMethod = propertyDescriptor.getReadMethod();

  // 如果属性是可读的,则输出属性值 - getter方法
  if (readMethod != null) {
    Object value = readMethod.invoke(bean);
    System.out.println(propertyName + "=" + value);
  }
}

4、设置 JavaBean 的属性值

可以通过 PropertyDescriptor 类的 getWriteMethod() 方法获取 JavaBean 的 setter 方法,然后通过反射机制调用setter方法设置属性值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 实例化 Person 对象 - JavaBean 对象
Person bean = new Person();

// 循环遍历 Person 对象的所有属性
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
  // 获取 Person 对象的属性名称
  String propertyName = propertyDescriptor.getName();

  // 获取 Person 对象的写方法 -- setter方法
  Method writeMethod = propertyDescriptor.getWriteMethod();

  // 匹配 Person 对象的属性,调用可写方法并设置相关的数据
  if ("name".equals(propertyName)) {
    writeMethod.invoke(bean, "zing");
  } else if ("age".equals(propertyName)) {
    writeMethod.invoke(bean, 18);
  }
}

System.out.println(bean);

或者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 实例化Person对象 - JavaBean对象
Person bean = Person.class.getConstructor().newInstance();

// 实例化属性描述器,并指定属性名称
PropertyDescriptor nameProperty = new PropertyDescriptor("name", Person.class);
PropertyDescriptor ageProperty = new PropertyDescriptor("age", Person.class);

// 获取 setter 方法,并调用 setter 方法初始化 JavaBean 对象
Method setNameMethod = nameProperty.getWriteMethod();
setNameMethod.invoke(bean, "李四");

ageProperty.getWriteMethod().invoke(bean,18);

System.out.println(bean);

四、Java内省的应用场景

Java内省广泛应用于Java GUI开发和Web开发中。在Java GUI开发中,常常需要对各种Swing组件进行布局、样式和事件处理等操作,而Java内省正是为此提供了非常便捷的方式。例如,我们可以使用Java内省获取一个JButton对象的所有属性和方法信息,并通过反射机制动态地修改和调用这些属性和方法,实现各种自定义功能。

在Web开发中,Java内省也被广泛应用于各种框架和组件中,例如Spring框架中的BeanWrapper和BeanUtils等工具类,以及Struts框架中的BeanUtils等工具类。这些工具类都是基于Java内省实现的,可以方便地对Java对象进行属性的读取、设置和复制等操作,减少了开发人员的工作量,提高了开发效率。

除了Java GUI和Web开发外,Java内省还有其他许多应用场景。例如,在Java数据库开发中,我们常常需要将ResultSet对象中的数据映射到Java对象中,而Java内省可以帮助我们动态地实现这一过程;在Java序列化和反序列化中,Java内省也被广泛应用于自定义的序列化和反序列化过程中,以实现更加灵活和高效的数据传输。

五、Java内省的优缺点

Java内省作为Java反射机制的一部分,具有许多优点和缺点。

1、优点

  1. 方便:Java内省提供了一种便捷的方式,可以通过BeanInfo、PropertyDescriptor和MethodDescriptor等类来动态地访问Java对象的属性和方法,而不需要手动编写大量的反射代码。
  2. 易用:Java内省提供了一组简单易用的API,使得开发人员可以方便地读取、设置和复制Java对象的属性。
  3. 高效:相比于纯反射机制,Java内省的性能更高,因为它缓存了Java类的属性和方法信息,并且避免了多次调用反射机制。
  4. 安全:Java内省在访问Java对象的属性和方法时,会对访问权限进行检查,确保访问的属性和方法是可访问的。

2、缺点

  1. 局限性:Java内省只能访问JavaBean中的属性和方法,无法访问其他类型的对象。
  2. 可读性差:由于Java内省需要使用PropertyDescriptor和MethodDescriptor等类来访问JavaBean中的属性和方法,因此代码可读性不如直接使用JavaBean的属性和方法。
  3. 不够灵活:由于Java内省是基于反射机制实现的,因此在某些场景下,需要使用纯反射机制来访问Java对象的属性和方法,才能实现更加灵活的操作。

六、总结

Java内省是Java反射机制的一部分,提供了一种便捷的方式来访问JavaBean中的属性和方法。通过BeanInfo、PropertyDescriptor和MethodDescriptor等类,我们可以动态地访问JavaBean中的属性和方法,并通过反射机制读取、设置和复制Java对象的属性。Java内省具有方便、易用、高效和安全等优点,但也存在一些局限性和缺点。在实际开发中,需要根据具体的业务场景选择合适的访问方式,以实现更加高效和灵活的操作。

在使用Java内省时,需要注意一些细节。例如,Java内省只能访问JavaBean中的属性和方法,因此需要遵循JavaBean的命名规范,并且将属性的get和set方法定义为public,以便Java内省能够访问。

此外,在访问JavaBean的属性和方法时,需要注意属性和方法的类型和参数类型是否匹配,以避免出现类型转换异常和其他运行时异常。

最后,Java内省虽然具有许多优点,但也不是万能的,有些场景下可能需要使用其他的访问方式。例如,在访问非JavaBean类型的对象或者需要实现更加灵活的访问时,可能需要使用纯反射机制或者其他的访问方式。

总之,Java内省作为Java反射机制的一部分,是Java开发中的一个重要工具。掌握Java内省的使用方法,可以帮助我们更加高效、方便地访问Java对象的属性和方法,提高开发效率,减少出错的可能性。

另外,Java内省还可以与其他Java技术结合使用,实现更加强大的功能。例如,可以将Java内省和JavaFX结合使用,实现JavaFX的数据绑定和动态UI更新。也可以将Java内省和Spring框架结合使用,实现Spring的依赖注入和动态代理等功能。

  • 26
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

꧁惜若༒奔已꧂

争取做到更好

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

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

打赏作者

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

抵扣说明:

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

余额充值