一、类
1.类的生命周期
1. 类的编译:通过 javac 命令将 .java 源文件编译成 .class 字节码文件。
2. 类的加载:类加载器(ClassLoader)将 .class 文件从硬盘加载到内存,形成“类对象”,包括加载、链接、初始化三个阶段。
3. 类的初始化和实例化:通过 new 关键字实例化类,创建具体对象。
4. 对象的生命周期:对象从创建到被垃圾回收器(GC)回收的整个过程。
5. 反射机制:允许在运行时动态加载类,扩展了类加载的灵活性。
二、 反射机制
Java 反射是指在运行时动态获取类的信息(如类名、方法、字段等)并操作类的能力。
三、反射的前提:获取 “类对象”
反射的所有操作,都必须基于一个核心载体 ——Class 类的对象(简称 “类对象”)。每个类在 JVM 中只会被加载一次,因此同一个类的类对象全局唯一。
1. 获取 Class 对象的三种方式
三种获取 Person 类的 Class 对象的方法
public class Test {public static void main(String[] args) throws ClassNotFoundException {// 1、硬盘阶段获取类对象Class class1 = Class.forName("com.qcby.reflect.Person");// 2、类阶段Class class2 = Person.class;// 3、对象阶段Person person = new Person();Class class3 = person.getClass();System.out.println(class1 == class2);System.out.println(class2 == class3);}
}
2.获取成员变量

2.1.读写变量值:set () 与 get ()
语法:
写值:field.set(对象实例, 变量值)(为指定对象的该变量赋值)
读值:field.get(对象实例)(获取指定对象的该变量值)
关键注意:若变量是 private 私有权限,直接读写会抛出 IllegalAccessException,需先调用 field.setAccessible(true) 开启 “暴力反射”,强制跳过权限检查。
2.2.应用
Person person = new Person();
// 获取类对象
Class pclass = Class.forName("com.qcby.reflect.Person");// 通过类对象获取成员变量们
Field[] fields = pclass.getDeclaredFields();
for (Field item : fields) {System.out.println(item);
}Field field_age = pclass.getDeclaredField("age");
System.out.println(field_age);Field[] public_fields = pclass.getFields();
for (Field item : public_fields) {System.out.println(item);
}Field field_from = pclass.getField("from");
System.out.println(field_from);// 获取和设置
field_from.set(person, "中国");
field_age.setAccessible(true);
field_age.set(person, 20);System.out.println(field_age.get(person));
System.out.println(field_from.get(person));
System.out.println(person);
// 1. 创建 Person 对象实例
Person person = new Person();
// 2. 获取类对象
Class pClass = Class.forName("reflect.Person");// 3. 获取所有成员变量(含 private)
Field[] allFields = pClass.getDeclaredFields();
for (Field field : allFields) {System.out.println(field); // 输出:private java.lang.String reflect.Person.name、private int reflect.Person.age、public java.lang.String reflect.Person.from
}// 4. 获取指定私有变量 name(需暴力反射)
Field nameField = pClass.getDeclaredField("name");
nameField.setAccessible(true); // 开启暴力反射,突破 private 限制
nameField.set(person, "赵嘉成"); // 赋值
System.out.println(nameField.get(person)); // 取值,输出:赵嘉成// 5. 获取指定公共变量 from(无需暴力反射)
Field fromField = pClass.getDeclaredField("from");
fromField.set(person, "中国"); // 直接赋值
System.out.println(person); // 输出:Person [name=赵嘉成, age=0, from=中国]
3.获取成员方法
3.1.执行方法:invoke ()
语法:method.invoke(对象实例, 方法参数值...)
若方法是静态方法(static),对象实例可传 null;
若方法无参数,参数值部分可省略或传空数组;
若方法有返回值,invoke() 会返回该值(需强转)。
权限注意:私有方法需先调用 method.setAccessible(true) 开启暴力反射。
3.2.执行代码
// 获取所有方法(包括私有)
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {System.out.println(method);
}// 调用私有方法
Method runMethod = clazz.getDeclaredMethod("run");
runMethod.setAccessible(true);
runMethod.invoke(person); // 执行方法
4.获取构造方法
构造方法的反射操作,核心是 “获取构造器” 和 “创建对象”(替代 new
关键字),支持通过无参 / 有参构造器创建实例。
返回类型 | 方法签名(含形参) | 获取范围 | 典型用途 |
Constructor<?>[] | getDeclaredConstructors() | 所有构造器(包括 private) | 暴力反射,想拿到任意访问修饰符的构造器 |
Constructor<T> | getDeclaredConstructor(Class<?>... parameterTypes) | 指定一个构造器(包括 private) | 暴力反射,精确匹配形参列表 |
Constructor<?>[] | getConstructors() | 仅 public 构造器 | 正常反射,只关心公开构造器 |
Constructor<T> | getConstructor(Class<?>... parameterTypes) | 指定一个 public 构造器 | 正常反射,精确匹配形参列表且为 public |
4.1 创建对象newInstance ()
语法:constructor.newInstance(构造参数值...)
无参构造器:参数值部分可省略,直接 constructor.newInstance();
有参构造器:需传入与参数列表匹配的参数值;
私有构造器:需先调用 constructor.setAccessible(true) 开启暴力反射。
// 1. 拿所有构造器(含 private)
Constructor<?>[] all = clazz.getDeclaredConstructors();// 2. 拿指定 private 构造器(如 Person(String, int))
Constructor<Person> c1 = clazz.getDeclaredConstructor(String.class, int.class);
c1.setAccessible(true); // 暴力反射
Person p1 = c1.newInstance("Tom", 20);// 3. 拿所有 public 构造器
Constructor<?>[] pub = clazz.getConstructors();// 4. 拿指定 public 无参构造器
Constructor<Person> c2 = clazz.getConstructor();
Person p2 = c2.newInstance();
四、其他配置
1.步骤拆解与代码
创建配置文件(refconfig.properties):
# 配置要加载的类(全类名)
reflect.className=reflect.Person
# 配置要调用的方法名
reflect.methodName=getAge
2.获取类的基本信息
需求 | API | 示例结果 |
全限定名(包 + 类) | clazz.getName() | com.qcby.reflect.Person |
简单类名 | clazz.getSimpleName() | Person |
包名 | clazz.getPackage().getName() | com.qcby.reflect |
父类 | clazz.getSuperclass() | class java.lang.Object |
实现的接口数组 | clazz.getInterfaces() | [interface java.io.Serializable] |
修饰符(public/private/…) | Modifier.toString(clazz.getModifiers()) | public |
是否为接口 | clazz.isInterface() | FALSE |
是否为数组 | clazz.isArray() | FALSE |
是否为枚举 | clazz.isEnum() | FALSE |
是否为注解 | clazz.isAnnotation() | FALSE |
是否为基本类型 | clazz.isPrimitive() | FALSE |
3.常见问题
1.反射能修改final字段吗?
可以,但需要先调用setAccessible(true)。
2.反射如何获取父类的方法?
使用getSuperclass()和getDeclaredMethods()。
3.反射为什么慢?
反射调用需要检查访问权限、动态解析方法名,无法被JIT优化。