Java 反射详解
反射简介
反射(Reflection) 是 Java 被视为动态语言的关键。反射机制允许程序在执行期借助 Reflection API 取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
加载完类之后,在堆内存的方法区中就产生了一个 Class 类型对象(一个类只有一个 Class 对象)。这个对象包含了完整的类的结构信息,可以通过这个对象看到类的结构。这个对象像一面镜子,透过镜子看到类的结构,所以形象地称为:反射。
Class 类
Class 对象概述
Class本身也是一个类。Class对象只能由系统建立。- 一个加载的类在 JVM 中只会有一个
Class实例。 - 一个
Class对象对应的是一个加载到 JVM 的.class文件。 - 每个类的实例都会记得自己是由哪个
Class实例所生成。 - 通过
Class可以完整地得到一个类中的所有被加载的结构。 Class类是 Reflection 的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象。
Class 类的常用方法
| 方法 | 说明 |
|---|---|
static Class<?> forName(String className) |
返回指定类名的 Class 对象 |
Object newInstance() |
调用缺省构造函数,返回该 Class 对象的一个实例 |
String getName() |
返回此 Class 对象所表示的实体名称(含包名) |
Class<?> getSuperclass() |
返回当前 Class 对象的父类的 Class 对象 |
Class<?>[] getInterfaces() |
获取当前 Class 对象的接口 |
ClassLoader getClassLoader() |
返回该类的类加载器 |
Method[] getMethods() |
返回包含某些 Method 对象的数组,反映此 Class 对象所表示的类或接口的公共方法 |
Field[] getFields() |
返回包含某些 Field 对象的数组,反映此 Class 对象所表示的类或接口的公共字段 |
Constructor<?>[] getConstructors() |
返回包含某些 Constructor 对象的数组,反映此 Class 对象所表示的类的所有公共构造器 |
更多方法可查阅 API 文档。
反射相关的主要 API
java.lang.Class 类与 java.lang.reflect 类库一起对反射的概念提供了支持。该类库包含了以下核心类(均实现了 Member 接口):
Field:代表类的成员变量Method:代表类的方法Constructor:代表类的构造器
这些类型的对象由 JVM 在运行时自动创建。
获取运行时类的完整结构
获取 Class 对象的四种方式
方式一:通过对象的 getClass() 方法
Student student = new Student();
Class c1 = student.getClass();
方式二:通过 Class.forName()(最常用)
Class c2 = null;
try {
c2 = Class.forName("text.Student"); // 必须使用全限定名(包名+类名)
} catch (ClassNotFoundException e) {
System.out.println("类未找到");
}
方式三:通过类名 .class
Class c3 = Student.class;
这种方式不会自动初始化该 Class 对象,因此更安全、性能更高。
方式四:基本内置类型的包装类有一个 TYPE 字段
Class<Integer> c4 = Integer.TYPE; // 表示 int 类型的 Class 对象
获得父类类型
Class c5 = c1.getSuperclass();
获取类的所有信息
// 获得 Class 对象
Class c1 = User.class;
// 获得类的名称
System.out.println(c1.getName()); // 包名 + 类名
System.out.println(c1.getSimpleName()); // 仅类名
// 获得类的属性
Field[] fs = c1.getFields(); // 只能获取 public 属性
fs = c1.getDeclaredFields(); // 获取所有属性(包括私有)
Field name = c1.getDeclaredField("name"); // 获取指定属性
// 获得类的方法
Method[] methods = c1.getMethods(); // 获取本类及其父类的所有 public 方法
methods = c1.getDeclaredMethods(); // 获取本类的所有方法(包括私有)
// 获取指定方法(需传入方法名及参数类型)
Method show = c1.getMethod("show", String.class);
// 获得构造器
Constructor[] constructors = c1.getConstructors(); // 所有 public 构造器
Constructor[] declaredConstructors = c1.getDeclaredConstructors(); // 所有构造器(包括私有)
// 获取指定构造器
Constructor constructor = c1.getDeclaredConstructor(String.class);
递归获取父类属性
Class tempClass = c1.getClass();
List<Field> fieldList = new ArrayList<>();
while (tempClass != null && !tempClass.getName().equals("java.lang.Object")) {
fieldList.addAll(Arrays.asList(tempClass.getDeclaredFields()));
tempClass = tempClass.getSuperclass(); // 递归获取父类
}
动态创建对象—通过反射
创建对象
Class c1 = User.class;
// 方式一:调用无参构造器
User user1 = (User) c1.newInstance();
// 方式二:通过指定构造器创建
Constructor constructor = c1.getDeclaredConstructor(String.class);
User user2 = (User) constructor.newInstance("wkf");
注意:
c1.newInstance()内部实际上调用了类的无参构造器,因此类必须拥有无参构造器且访问权限足够。
调用方法
User user3 = (User) c1.newInstance();
Method show = c1.getDeclaredMethod("show", String.class);
show.invoke(user3, "锵锵锵"); // 激活方法
操作属性
User user4 = (User) c1.newInstance();
Field name = c1.getDeclaredField("name");
name.setAccessible(true); // 关闭权限检测,允许访问私有属性
name.set(user4, "wkf");
setAccessible 与性能
Method、Field、Constructor对象都有setAccessible(boolean flag)方法。- 作用:启动或禁用访问安全检查的开关。
true:取消 Java 语言访问检查,可访问私有成员,同时提高反射效率。false:实施访问检查。
性能对比:关闭访问检查后,反射调用的性能可大幅提升。
反射操作泛型
Java 采用泛型擦除机制,泛型信息仅在编译期有效,运行时会被擦除。为了通过反射操作泛型,Java 提供了几个新类型来表示泛型信息:
ParameterizedType:参数化类型,如List<String>GenericArrayType:元素类型是参数化类型或类型变量的数组,如List<String>[]TypeVariable:类型变量,如TWildcardType:通配符类型,如? extends Number
示例:获取方法参数的泛型信息
Method method = User.class.getMethod("test01", Map.class, List.class);
Type[] genericParameterTypes = method.getGenericParameterTypes();
for (Type t : genericParameterTypes) {
System.out.println(t);
if (t instanceof ParameterizedType) {
Type[] actualTypeArguments = ((ParameterizedType) t).getActualTypeArguments();
for (Type tt : actualTypeArguments) {
System.out.println(" 泛型类型: " + tt);
}
}
}
反射操作注解
通过反射可以在运行时获取类、方法、字段上的注解信息,常用于框架开发(如 ORM)。
示例:自定义注解与反射读取
// 定义注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Tablex {
String value();
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Fieldx {
String col();
String type();
int length();
}
// 使用注解
@Tablex("db_people")
class People {
@Fieldx(col = "id", type = "int", length = 10)
private int id;
@Fieldx(col = "name", type = "varchar", length = 3)
private String name;
@Fieldx(col = "age", type = "int", length = 10)
private int age;
// 构造器、getter/setter、toString 省略
}
// 通过反射获取注解信息
public class TestAnnotation {
public static void main(String[] args) throws Exception {
Class<People> clazz = People.class;
// 获取类上的注解
Tablex table = clazz.getAnnotation(Tablex.class);
System.out.println("表名: " + table.value());
// 获取字段上的注解
Field field = clazz.getDeclaredField("id");
Fieldx fieldx = field.getAnnotation(Fieldx.class);
System.out.println("字段列: " + fieldx.col() + ", 类型: " + fieldx.type() + ", 长度: " + fieldx.length());
}
}
总结
- 反射提供了在运行时动态获取类信息、创建对象、调用方法、访问字段的能力。
- 核心是
Class类及java.lang.reflect包下的Field、Method、Constructor。 - 反射会带来一定的性能开销,但通过
setAccessible(true)可以提升效率。 - 反射广泛用于框架(如 Spring、MyBatis)、注解解析、动态代理等场景。
注意:反射虽然强大,但过度使用会破坏封装性,且存在安全风险,应合理使用。

浙公网安备 33010602011771号