引言
Java反射是Java语言中的一种动态机制,它允许在运行时检查和操作类的结构和行为。反射的强大功能使得程序可以在运行时动态加载类、调用方法和访问字段,从而极大地增强了Java程序的灵活性和扩展性。
反射(Reflection)是指程序在运行时能够自我检查和操作自身的能力。通过反射,可以获取类的构造器、方法、字段等信息,并能动态调用对象的方法、设置或获取对象的字段值。
反射关键信息
Class
: 代表类的实体,在运行时加载类时会创建对应的Class对象。Constructor
: 代表类的构造方法。Method
: 代表类的方法。Field
: 代表类的字段。Java反射
最核心的类位于JDK源码 java.lang.reflect
包下,比如Class、Constructor、Field 和 Method
等,他们提供了对类和对象运行时信息进行检查和操作的方法。
Java反射的核心在于Class
类,它包含了关于类的所有信息。在Java虚拟机(JVM
)加载类时,会为每个类创建一个对应的Class
对象,该对象保存了类的元数据。通过这些元数据,程序可以在运行时获取类的详细信息并进行操作。
主要可以从下面 4个点来阐述:
Class对象
,Class对象包含了类的元数据信息,并提供了访问和操作类的接口。Class对象
获取类的字段、方法、构造函数等信息,使用Field
类和Method
类来访问和操作字段和方法,甚至可以调用私有的字段和方法。通过上述的分析可以看出:反射机制需要基于Java虚拟机
对类的加载、存储和访问机制的支持,通过反射,可以在运行时动态地探索和操作类的信息,实现灵活的编程和代码的动态行为。
很多优秀的框架内部都使用了Java反射
,这里重点讲解下给 Java打下半壁江山的 Spring生态(Spring Framework,Spring MVC,SpringBoot, SpringCloud...),以 Spring Framework为例:
另外,还有一些耳熟能详的框架也使用了Java反射
:
总结以下几点:
Class clazz = Class.forName("com.example.MyClass");
// 或者
Class clazz = MyClass.class;
// 或者
Class clazz = myObject.getClass();
Constructor constructor = clazz.getConstructor(String.class);
Object instance = constructor.newInstance("example");
Method method = clazz.getMethod("myMethod", String.class);
method.invoke(instance, "Hello");
Field field = clazz.getDeclaredField("myField");
// 允许访问私有字段
field.setAccessible(true);
field.set(instance, "New Value");
String value = (String) field.get(instance);
@Slf4j
public class ReflectionUtil {
/**
* 获取属性名以及对应的属性值
*
* @param o 对象
* @return map
*/
public static Map getFieldNameAndValue(Object o) {
Map resMap = new LinkedHashMap<>();
Class clazz = o.getClass();
while (Objects.nonNull(clazz)) {
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
String fieldName = field.getName();
if (Objects.isNull(resMap.get(fieldName))) {
resMap.put(fieldName, getFieldValueByName(fieldName, o));
}
}
clazz = clazz.getSuperclass();
}
return resMap;
}
/**
* 获取属性名以及对应的属性值
*
* @param list 对象数组
* @return list\
*/
public static List> getFieldNameAndValueMaps(List list) {
List> resMaps = new ArrayList<>(list.size());
for (Object o : list) {
if (Objects.isNull(o)) {
throw new RuntimeException("Arrays Cannot Contain Null... ...");
}
Map fieldNameAndValueMap = getFieldNameAndValue(o);
resMaps.add(fieldNameAndValueMap);
}
return resMaps;
}
public static List getFieldNames(Class clazz) {
List fieldList = new ArrayList<>();
while (Objects.nonNull(clazz)) {
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
String fieldName = field.getName();
fieldList.add(fieldName);
}
clazz = clazz.getSuperclass();
}
return fieldList;
}
/**
* 获取类上的指定字段。如果在类本身上找不到该字段,则将递归检查超类。
*
* @param clazz source class
* @param fieldName 字段名
* @return 字段
*/
private static Field getField(Class clazz, String fieldName) {
try {
return clazz.getDeclaredField(fieldName);
} catch (NoSuchFieldException nsf) {
if (clazz.getSuperclass() != null) {
return getField(clazz.getSuperclass(), fieldName);
}
throw new IllegalStateException("Could not locate field '" + fieldName + "' on class " + clazz);
}
}
/**
* 根据属性名获取属性值
*
* @param fieldName 属性名称
* @param o 对象
* @return Object
*/
public static Object getFieldValueByName(String fieldName, Object o) {
try {
String firstLetter = fieldName.substring(0, 1).toUpperCase();
String getter = "get" + firstLetter + fieldName.substring(1);
Method method = o.getClass().getMethod(getter);
return method.invoke(o);
} catch (Exception e) {
log.info("根据属性名获取属性值异常:" + e.getMessage() + "\n" + e);
return null;
}
}
@SuppressWarnings({"all"})
public static void setFiledValue(Object bean, String filedName, Object value) throws NoSuchFieldException, IllegalAccessException {
Class aClass = bean.getClass();
Field field = getField(aClass, filedName);
field.setAccessible(true);
field.set(bean, value);
}
}
反射的实现依赖于JVM提供的本地方法接口(JNI),通过调用本地方法实现对类信息的获取和操作。
public static Class forName(String className) throws ClassNotFoundException {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if (classLoader == null) {
classLoader = ClassLoader.getSystemClassLoader();
}
return classLoader.loadClass(className);
}
public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class caller = Reflection.getCallerClass();
checkAccess(caller, clazz, obj, modifiers);
}
}
MethodAccessor ma = methodAccessor; // read volatile
if (ma == null) {
ma = acquireMethodAccessor();
}
return ma.invoke(obj, args);
}
从源码可以看出:Method.invoke()
方法,真实返回的是接口MethodAccessor.invoke()
方法。MethodAccessor
接口有三个实现类,具体是调用哪个类的 invoke 方法?
跟到源码最后可以发现:Method.invoke()
方法最终调用 native的invoke0()
,应用层面的操作最终转换成对操作系统 c/c++方法的调用。
反射机制
在 Java中的作用不言而喻,下面列举了反射机制的一些常见场景和原因:
Java反射是一个强大的工具,极大地增强了Java语言的动态性和灵活性。然而,在使用反射时需要权衡其性能开销和安全风险。Java反射
有优点也有缺点,从整体上看,Java反射
是以牺牲了小部分的性能换取了更好的扩展性和灵活性,牺牲小我成就大我
,而且,随着现代硬件设备能力越来越强,这点小性能的牺牲是完全值得的。理解反射的原理和使用场景,可以更好地应用反射技术来解决实际开发中的问题。