反射的真正价值在于处理编译时未知的类型,从而编写更通用化的代码。
一、反射基础概念
(一)什么是反射
反射是 Java 的一种机制,它允许程序在运行时获取任何已知名称的类的内部信息,包括类的属性、方法、构造函数等,并且能够在运行时操作这些成员。通过反射,Java 程序可以实现动态加载类、创建对象、调用方法等操作,这在许多场景下提供了极大的灵活性。
(二)反射的核心类
在 Java 反射体系中,Class类处于核心与基础的地位 ,它代表一个类的运行时实例。当 JVM 加载类时,会为每个类创建对应的Class对象,这个对象包含了类的完整元数据,像是类名、继承结构、实现的接口,以及类所拥有的属性、方法和构造函数等关键信息。通过Class类,可以获取到类的各种信息。
而Field、Method和Constructor类,则是依赖于Class类来获取并对类的不同成员进行具体操作。
- Field类:用于表示类的属性,通过Class类的getFields()(获取公共属性)或getDeclaredFields()(获取所有属性,包括私有属性)等方法获得Field实例后,便能够获取和设置属性的值。例如,使用field.get(obj)获取对象obj的属性值,field.set(obj, value)设置对象obj的属性值为value。
- Method类:用于表示类的方法,借助Class类的getMethods()(获取公共方法)或getDeclaredMethods()(获取所有方法,包括私有方法)等方法获取Method实例后,就可以通过method.invoke(obj, args)来调用方法,其中obj是方法所属的对象实例,args是方法参数,以此执行相应的业务逻辑。
- Constructor类:用于表示类的构造函数,通过Class类的getConstructors()(获取公共构造函数)或getDeclaredConstructors()(获取所有构造函数,包括私有构造函数)等方法获取Constructor实例后,能够使用constructor.newInstance(args)来创建类的实例,args为构造函数的参数。
二、反射的原理
Java 反射的实现依赖于 Java 虚拟机(JVM)在运行时对类的加载和管理机制。当 JVM 加载一个类时,会在内存中创建一个Class对象,这个对象包含了类的所有元数据信息,如类名、父类、接口、属性、方法等。反射机制就是通过操作这个Class对象及其相关的Field、Method、Constructor等对象来实现对类的动态操作。
三、获取 Class 的三种方式
(一)通过Class.forName(String className)
这是一种最常用的方式,它接收一个类的全限定名(包名 + 类名)作为参数,返回对应的Class对象。例如:
try {
Class<?> clazz = Class.forName("com.example.MyClass");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
这种方式的好处是可以根据字符串动态加载类,常用于需要在运行时根据配置或其他条件决定加载哪个类的场景。
(二)通过对象的getClass()方法
如果已经有一个对象实例,可以通过调用其getClass()方法来获取对应的Class对象。例如:
MyClass myObject = new MyClass();
Class<?> clazz = myObject.getClass();
这种方式适用于已经有对象,需要获取其运行时类型信息的情况。
(三)通过类字面量ClassName.class
直接使用类的字面量形式获取Class对象,这种方式最为简洁直观。例如:
Class<?> clazz = MyClass.class;
这种方式在编译时就确定了类,通常用于在代码中明确知道类的情况下获取其Class对象。
四、如何操作类
- 以下getXXX()方法,空参则获得对应结果的数组,指定参数(参数名)则获得单个变量。
- getDeclaredXXX()方法,获取到的是所有成员,无关访问权限,不带Deaclared则获取的是public的成员。
- 在获取到
Field
对象后,需要调用field.setAccessible(true)
来打破封装,才允许访问私有成员,
- 在获取到
(一)获取类的属性
通过Class对象的getFields()方法可以获取类的所有公共属性,返回一个Field数组。如果需要获取包括私有属性在内的所有属性,可以使用getDeclaredFields()方法。例如:
Class<?> clazz = MyClass.class;
Field[] fields = clazz.getFields();
for (Field field : fields) {
System.out.println(field.getName());
}
(二)获取类的方法
使用Class对象的getMethods()方法获取类的所有公共方法,getDeclaredMethods()方法获取所有方法(包括私有方法)。例如:
Class<?> clazz = MyClass.class;
Method[] methods = clazz.getMethods();
for (Method method : methods) {
System.out.println(method.getName());
}
(三)获取类的构造函数
通过Class对象的getConstructors()方法获取类的所有公共构造函数,getDeclaredConstructors()方法获取所有构造函数(包括私有构造函数)。例如:
Class<?> clazz = MyClass.class;
Constructor<?>[] constructors = clazz.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println(constructor.getName());
}
(四)获取类的注解
- 获取类上的所有注解:可以使用Class对象的getAnnotations()方法,该方法会返回一个包含类上所有注解的数组。例如:
import java.lang.annotation.Annotation;
class MyClass {
// 空类,仅用于示例
}
public class GetClassAnnotations {
public static void main(String[] args) {
Class<?> clazz = MyClass.class;
Annotation[] annotations = clazz.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println("类上的注解: " + annotation.annotationType().getName());
}
}
}
- 获取类上特定类型的注解:使用Class对象的getAnnotation(Class<T> annotationClass)方法,传入想要获取的注解类型的Class对象,即可获取该类型的注解。如果类上不存在该类型的注解,则返回null。例如:
import java.lang.annotation.Annotation;
@interface MyAnnotation {
String value();
}
@MyAnnotation("示例值")
class MyAnnotatedClass {
// 空类,仅用于示例
}
public class GetSpecificClassAnnotation {
public static void main(String[] args) {
Class<?> clazz = MyAnnotatedClass.class;
MyAnnotation myAnnotation = clazz.getAnnotation(MyAnnotation.class);
if (myAnnotation!= null) {
System.out.println("获取到的特定注解的值: " + myAnnotation.value());
}
}
}
五、如何操作对象
注:此处的对象也可以是从外部获取的,而并不是只有以下示例中通过类的反射的构造器新创建的对象。
(一)创建对象
通过Constructor对象的newInstance()方法可以创建类的实例。如果类有多个构造函数,可以根据参数类型选择合适的构造函数。例如:
Class<?> clazz = MyClass.class;
Constructor<?> constructor = clazz.getConstructor();
Object instance = constructor.newInstance();
(二)访问和修改对象的属性
获取Field对象后,通过field.get(Object obj)方法可以获取对象的属性值,field.set(Object obj, Object value)方法可以设置对象的属性值。需要注意的是,如果属性是私有的,需要先调用field.setAccessible(true)来打破封装。例如:
Class<?> clazz = MyClass.class;
Object instance = clazz.getConstructor().newInstance();
Field field = clazz.getDeclaredField("privateField");
field.setAccessible(true);
field.set(instance, "new value");
Object value = field.get(instance);
(三)调用对象的方法
获取Method对象后,通过method.invoke(Object obj, Object… args)方法可以调用对象的方法,其中args是方法的参数。例如:
Class<?> clazz = MyClass.class;
Object instance = clazz.getConstructor().newInstance();
Method method = clazz.getMethod("myMethod", String.class);
method.invoke(instance, "parameter");