文章目录
- 简介
- 作用
- JDK API
- 根据枚举名称返回枚举中的数据
简介
Java反射(Reflection)是指在运行时能够获取类的信息、创建对象、调用方法、访问属性等。它允许程序在运行时动态地操作一个类,而不需要提前知道这个类的名称或者结构。反射机制主要用于框架开发和底层库的实现。
作用
Java反射的主要作用有以下几点:
- 在运行时获取类的信息,如类名、构造方法、成员变量、成员方法等。
- 创建对象实例,可以通过Class类的newInstance()方法或者Constructor类的newInstance()方法来创建对象。
- 调用方法,可以通过Method类的invoke()方法来调用指定对象的方法。
- 访问属性,可以通过Field类的get()方法和set()方法来访问和修改对象的私有属性。
- 实现动态代理,通过反射机制可以方便地实现动态代理设计模式。
- 实现框架和底层库的功能,如Spring框架、Hibernate框架等都大量使用了反射机制。
JDK API
在JDK中,反射相关的API可以分为以下几个方面:
- 获取反射的Class对象:这是反射中的基础步骤,要获取一个类或调用一个类的方法,我们首先需要获取到该类的 Class 对象。
Class<?> clazz = Class.forName("java.util.ArrayList");
System.out.println(clazz);
- 通过反射创建类对象:可以使用Constructor类的newInstance()方法或者Class类的newInstance()方法来创建对象实例。
Object obj = clazz.newInstance();
- 通过反射获取类属性方法及构造器:可以使用Class类的getDeclaredFields()、getDeclaredMethods()和getDeclaredConstructors()等方法来获取类的属性、方法和构造器。
public static void main(String[] args)
throws Exception {
Class<?> clazz = Class.forName("java.util.ArrayList");
System.out.println(Arrays.toString(clazz.getDeclaredFields()));
System.out.println(Arrays.toString(clazz.getDeclaredMethods()));
System.out.println(Arrays.toString(clazz.getDeclaredConstructors()));
}
- 访问属性:可以通过Field类的get()方法和set()方法来访问和修改对象的私有属性。
示例:创建了一个Person对象,并使用反射修改了其name字段的值:
public class ReflectionDemo {
public static void main(String[] args) {
try {
// 创建一个Person对象
Person person = new Person("张三", 25);
// 获取Person类的Class对象
Class<?> clazz = person.getClass();
// 获取name字段
Field nameField = clazz.getDeclaredField("name");
// 设置name字段可访问
nameField.setAccessible(true);
// 设置name字段值为"李四"
nameField.set(person, "李四");
// 输出修改后的Person对象
System.out.println(person);
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
}
}
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
- 调用方法:可以通过Method类的invoke()方法来调用指定对象的方法。
Class<?> clazz = Class.forName("java.util.ArrayList");
Object obj = clazz.newInstance();
Method method = clazz.getDeclaredMethod("add", Object.class);
method.invoke(obj, "Hello");
System.out.println(obj);
根据枚举名称返回枚举中的数据
系统中普遍存在枚举类,枚举类主要用于表示固定数量的可能值,枚举类在需要表示一组固定值的场景中非常有用,比如订单的状态分为已下单和未下单,erp分为出库和入库。我们根据上面的知识最后实现一个扫描指定路径的,根据枚举名称返回枚举中数据的示例:
@Slf4j
public class EnumUtil {
public static Map<String, List<DictionaryVo>> enums = null;
static {
try {
enums = getEnums();
} catch (InvocationTargetException | IllegalAccessException e) {
log.error("error enum to vo", e);
}
}
// 扫描的包路径
private static final String basePackage = "com.jianguozh.anpin.common.constant";
private static Map<String, List<DictionaryVo>> getEnums() throws InvocationTargetException, IllegalAccessException {
//扫描该包路径下所有class文件
Set<Class<?>> classes = ClassUtil.scanPackage(basePackage);
Map<String, List<DictionaryVo>> enums = new HashMap<>();
List<DictionaryVo> list;
for (Class<?> aClass : classes) {
if (ClassUtil.isEnum(aClass)) {
Method keyMethod = ClassUtil.getDeclaredMethod(aClass, "getCode");
Method valueMethod = ClassUtil.getDeclaredMethod(aClass, "getDisplayName");
if (keyMethod == null || valueMethod == null) {
continue;
}
Object[] objs = aClass.getEnumConstants();
// 装载当前枚举所有值
list = new ArrayList<>();
for (Object obj : objs) {
String code = keyMethod.invoke(obj).toString();
String val = valueMethod.invoke(obj).toString();
list.add(new DictionaryVo(code, val));
}
// 装载当前枚举list
String enumName = aClass.getName()
.substring(aClass.getName().lastIndexOf(".") + 1);
enums.put(enumName, list);
}
}
return enums;
}
}