你好,我是看山。
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准备的,主要包括下面几个类:
- Introspector类:这是内省API的核心类,提供了获取BeanInfo对象的方法,例如Introspector.getBeanInfo()方法。
- BeanInfo类:这个类包含了关于一个对象的所有Bean属性信息,包括属性的描述符(PropertyDescriptor)。
- 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赋值方面,两者的性能差异有多少。