1. 反射(Reflection)

1.1 反射的概述

反射是Java语言的核心特性之一,它允许程序在运行状态下动态获取类的信息并操作类的成员(构造方法、成员变量、成员方法)。

专业定义
  • 对于任意一个类,都能够知道这个类的所有属性和方法
  • 对于任意一个对象,都能够调用它的任意属性和方法
  • 这种动态获取信息以及动态调用对象方法的功能称为Java反射机制
通俗理解(核心)
  1. 无视访问修饰符:通过反射创建的对象可以访问类中私有(private)、保护(protected)等修饰的成员
  2. 配置化开发:可与配置文件结合,动态创建对象和调用方法,需求变更时无需修改代码,仅需修改配置文件

1.2 反射的学习重点

反射的本质是操作Class字节码文件对象,核心学习内容包括:

  1. 如何获取Class字节码文件对象
  2. 如何通过反射获取并使用构造方法(创建对象)
  3. 如何通过反射获取并操作成员变量(赋值、取值)
  4. 如何通过反射获取并调用成员方法(执行方法)

1.3 获取Class字节码文件对象的三种方式

Class字节码文件对象是反射的入口,在JVM内存中唯一(一个类只有一个Class对象)。

获取方式

代码示例

适用场景

Class.forName(“全类名”)

Class clazz = Class.forName("com.itheima.Student");

最常用,适用于配置文件加载(全类名可配置)

类名.class

Class clazz = Student.class;

已知具体类,编译期确定类型

对象.getClass()

Student s = new Student(); Class clazz = s.getClass();

已知对象实例,需先创建对象

代码验证(唯一性)
// 方式1:Class.forName
Class clazz1 = Class.forName("com.itheima.reflectdemo.Student");
// 方式2:类名.class
Class clazz2 = Student.class;
// 方式3:对象.getClass()
Student s = new Student();
Class clazz3 = s.getClass();
// 验证内存中唯一
System.out.println(clazz1 == clazz2);
// true
System.out.println(clazz2 == clazz3);
// true

1.4 字节码文件与字节码文件对象

  • Java文件:开发者编写的.java源代码文件
  • 字节码文件:Java文件编译后生成的.class文件(硬盘中真实存在)
  • 字节码文件对象.class文件加载到JVM内存后,虚拟机自动创建的Class对象(内存中唯一,包含类的所有信息)

1.5 通过反射获取构造方法

反射提供了4种获取构造方法的API,核心规则:

  • get:获取公开(public)成员
  • getDeclared:获取所有成员(包含私有)
  • 复数形式(Constructors):获取所有构造方法
  • 私有构造需通过setAccessible(true)临时修改访问权限(暴力反射)

方法名

说明

Constructor<?>[] getConstructors()

获取所有公开构造方法

Constructor<?>[] getDeclaredConstructors()

获取所有构造方法(含私有)

Constructor<T> getConstructor(Class<?>... parameterTypes)

获取指定公开构造方法(参数为参数类型的Class对象)

Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)

获取指定构造方法(含私有)

代码示例
public class ReflectDemo2
{
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
// 1. 获取Class对象
Class clazz = Class.forName("com.itheima.reflectdemo.Student");
// 2. 获取所有公开构造方法
Constructor[] constructors1 = clazz.getConstructors();
for (Constructor constructor : constructors1) {
System.out.println(constructor);
}
// 3. 获取所有构造方法(含私有)
System.out.println("=======================");
Constructor[] constructors2 = clazz.getDeclaredConstructors();
for (Constructor constructor : constructors2) {
System.out.println(constructor);
}
// 4. 获取指定公开构造方法
System.out.println("=======================");
Constructor con1 = clazz.getConstructor();
// 空参构造
Constructor con2 = clazz.getConstructor(String.class,
int.class)
;
// 带参构造
System.out.println(con1);
System.out.println(con2);
// 5. 获取指定私有构造方法
System.out.println("=======================");
Constructor con3 = clazz.getDeclaredConstructor(String.class)
;
// 私有单参构造
System.out.println(con3);
}
}

1.6 通过反射创建对象(构造方法的使用)

通过Constructor对象的newInstance()方法创建对象,步骤如下:

  1. 获取Class对象
  2. 获取目标构造方法(公开/私有)
  3. 私有构造需调用setAccessible(true)
  4. 调用newInstance(参数)创建对象
代码示例(Student类见下文)
public class ReflectDemo3
{
public static void main(String[] args) throws Exception {
// 1. 获取Class对象
Class clazz = Class.forName("com.itheima.reflectdemo.Student");
// 需求1:通过空参构造创建对象
System.out.println("=== 空参构造创建对象 ===");
Constructor con1 = clazz.getConstructor();
// 获取空参构造
Student stu1 = (Student) con1.newInstance();
// 创建对象
System.out.println(stu1);
// 需求2:通过私有带参构造创建对象
System.out.println("=== 私有带参构造创建对象 ===");
Constructor con2 = clazz.getDeclaredConstructor(String.class,
int.class)
;
// 获取私有构造
con2.setAccessible(true);
// 暴力反射:临时修改访问权限
Student stu2 = (Student) con2.newInstance("张三", 23);
// 创建对象
System.out.println(stu2);
}
}
// 对应的Student类
class Student
{
private String name;
private int age;
// 公开空参构造
public Student() {
}
// 私有带参构造
private Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{name='" + name + "', age=" + age + "}";
}
}

1.7 通过反射获取成员变量

获取成员变量的API规则与构造方法一致,核心类为java.lang.reflect.Field

方法名

说明

Field[] getFields()

获取所有公开成员变量

Field[] getDeclaredFields()

获取所有成员变量(含私有)

Field getField(String name)

获取指定公开成员变量(参数为变量名)

Field getDeclaredField(String name)

获取指定成员变量(含私有)

代码示例
public class ReflectDemo4
{
public static void main(String[] args) throws Exception {
// 1. 获取Class对象
Class clazz = Class.forName("com.itheima.reflectdemo.Student");
// 2. 获取所有公开成员变量
Field[] fields1 = clazz.getFields();
System.out.println("=== 所有公开成员变量 ===");
for (Field field : fields1) {
System.out.println(field);
}
// 3. 获取所有成员变量(含私有)
System.out.println("=== 所有成员变量(含私有) ===");
Field[] fields2 = clazz.getDeclaredFields();
for (Field field : fields2) {
System.out.println(field);
}
// 4. 获取指定公开成员变量
System.out.println("=== 指定公开成员变量 ===");
Field genderField = clazz.getField("gender");
System.out.println(genderField);
// 5. 获取指定私有成员变量
System.out.println("=== 指定私有成员变量 ===");
Field nameField = clazz.getDeclaredField("name");
System.out.println(nameField);
}
}
// 对应的Student类
class Student
{
private String name;
// 私有变量
private int age;
// 私有变量
public String gender;
// 公开变量
public String address;
// 公开变量
// 构造方法省略...
}

1.8 通过反射操作成员变量(赋值与取值)

通过Field对象的set()get()方法操作成员变量:

  • void set(Object obj, Object value):给obj对象的当前变量赋值为value
  • Object get(Object obj):获取obj对象的当前变量值
代码示例
public class ReflectDemo5
{
public static void main(String[] args) throws Exception {
// 1. 创建Student对象
Student s = new Student("张三", 23, "广州");
// 2. 获取Class对象
Class clazz = Class.forName("com.itheima.reflectdemo.Student");
// 3. 获取私有成员变量name
Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true);
// 暴力反射
// 4. 修改name的值
nameField.set(s, "王五");
// 给s对象的name赋值为"王五"
// 5. 获取name的值
String newName = (String) nameField.get(s);
System.out.println("修改后的name:" + newName);
System.out.println("修改后的对象:" + s);
}
}

1.9 通过反射获取成员方法

获取成员方法的API规则与前两者一致,核心类为java.lang.reflect.Method

方法名

说明

Method[] getMethods()

获取所有公开成员方法(含父类继承的公开方法)

Method[] getDeclaredMethods()

获取所有成员方法(含私有,仅当前类)

Method getMethod(String name, Class<?>... parameterTypes)

获取指定公开方法(参数1:方法名;参数2:方法参数类型的Class对象)

Method getDeclaredMethod(String name, Class<?>... parameterTypes)

获取指定方法(含私有)

代码示例
public class ReflectDemo6
{
public static void main(String[] args) throws Exception {
// 1. 获取Class对象
Class clazz = Class.forName("com.itheima.reflectdemo.Student");
// 2. 获取所有公开方法(含父类)
System.out.println("=== 所有公开方法(含父类) ===");
Method[] methods1 = clazz.getMethods();
for (Method method : methods1) {
System.out.println(method);
}
// 3. 获取所有方法(含私有,仅当前类)
System.out.println("=== 所有方法(含私有) ===");
Method[] methods2 = clazz.getDeclaredMethods();
for (Method method : methods2) {
System.out.println(method);
}
// 4. 获取指定公开方法
System.out.println("=== 指定公开方法(eat) ===");
Method eatMethod = clazz.getMethod("eat", String.class)
;
// 带参方法
System.out.println(eatMethod);
// 5. 获取指定私有方法
System.out.println("=== 指定私有方法(study) ===");
Method studyMethod = clazz.getDeclaredMethod("study");
// 空参方法
System.out.println(studyMethod);
}
}
// 对应的Student类
class Student
{
// 私有方法
private void study() {
System.out.println("学生在学习");
}
// 公开带参方法
public String eat(String food) {
System.out.println("学生在吃" + food);
return "吃饱了";
}
}

1.10 通过反射调用成员方法

通过Method对象的invoke()方法调用方法:

  • Object invoke(Object obj, Object... args):调用obj对象的当前方法,参数为args
  • 返回值:方法的返回值(无返回值则为null)
代码示例
public class ReflectDemo7
{
public static void main(String[] args) throws Exception {
// 1. 创建Student对象
Student s = new Student();
// 2. 获取Class对象
Class clazz = Class.forName("com.itheima.reflectdemo.Student");
// 3. 获取指定方法(eat方法)
Method eatMethod = clazz.getMethod("eat", String.class)
;
// 4. 调用方法
String result = (String) eatMethod.invoke(s, "重庆小面");
// 传递参数
System.out.println("方法返回值:" + result);
// 5. 调用私有方法(study方法)
Method studyMethod = clazz.getDeclaredMethod("study");
studyMethod.setAccessible(true);
// 暴力反射
studyMethod.invoke(s);
// 无参方法,无需传递args
}
}

1.11 反射面试题:反射的优缺点

优点
  1. 灵活性高:可动态创建对象和调用方法,适用于框架开发(如Spring、MyBatis)
  2. 无视修饰符:可访问私有成员,方便底层框架实现(如ORM框架操作私有字段)
  3. 配置化开发:结合配置文件,需求变更无需修改代码(如修改配置文件切换实现类)
缺点
  1. 性能损耗:反射操作需要解析字节码,性能比直接调用低(约为直接调用的1/100)
  2. 安全性问题:破坏封装性,可能导致非法访问(如修改私有字段)
  3. 代码可读性差:反射代码较繁琐,调试难度高

1.12 反射实战1:泛型擦除

核心概念

集合的泛型仅在编译期有效,编译为.class文件后,泛型信息会被擦除(即JVM运行时不识别泛型)。通过反射可绕过泛型限制,向泛型集合中添加任意类型数据。

代码示例
public class ReflectDemo8
{
public static void main(String[] args) throws Exception {
// 1. 创建泛型集合(指定Integer类型)
ArrayList<
Integer> list = new ArrayList<
>();
list.add(123);
// 正常添加Integer类型
// list.add("abc"); // 编译报错,泛型限制
// 2. 通过反射绕过泛型限制
Class clazz = list.getClass();
// 获取add方法(泛型擦除后,参数类型为Object)
Method addMethod = clazz.getMethod("add", Object.class)
;
// 调用add方法添加String类型
addMethod.invoke(list, "abc");
// 3. 打印集合(包含Integer和String)
System.out.println(list);
// 输出:[123, abc]
}
}

1.13 反射实战2:修改字符串内容

字符串不可变的本质

字符串底层由private final byte[] value数组存储,不可变的原因:

  1. private:外部无法直接访问value数组
  2. final:value数组的地址值不可修改
  3. get/set方法:外部无法操作value数组
反射修改字符串(原理)

通过反射获取value字段,修改数组内部元素(不改变数组地址值,规避final限制)。

代码示例(注意:JDK 9+已屏蔽此操作)
public class ReflectDemo9
{
public static void main(String[] args) throws Exception {
String s = "abc";
String ss = "abc";
// 与s共享同一个字符数组
// 1. 获取String的Class对象
Class clazz = s.getClass();
// 2. 获取value字段(私有final)
Field valueField = clazz.getDeclaredField("value");
valueField.setAccessible(true);
// 暴力反射
// 3. 修改value数组的内容
byte[] value = (byte[]) valueField.get(s);
value[0] = 100;
// 'a' -> 'd'
// 4. 打印结果(两个字符串均被修改)
System.out.println(s);
// 输出:dbc
System.out.println(ss);
// 输出:dbc
}
}

1.14 反射实战3:结合配置文件动态开发(重点)

需求

通过配置文件指定类名和方法名,动态创建对象并调用方法,需求变更时无需修改代码。

实现步骤
  1. 创建配置文件(prop.properties
  2. 加载配置文件,读取类名和方法名
  3. 通过反射创建对象并调用方法
代码示例
public class ReflectDemo10
{
public static void main(String[] args) throws Exception {
// 1. 加载配置文件
Properties prop = new Properties();
prop.load(new FileInputStream("prop# Java反射与动态代理详解:从原理到实践
## 1. 反射(Reflection)
### 1.1 反射的概述
反射是Java语言的核心特性之一,它允许程序在**运行状态**下动态获取类的信息并操作类的成员(构造方法、成员变量、成员方法)。
#### 专业定义
- 对于任意一个类,都能够知道这个类的所有属性和方法
- 对于任意一个对象,都能够调用它的任意属性和方法
- 这种动态获取信息以及动态调用对象方法的功能称为Java反射机制
#### 通俗理解(核心)
1. **无视访问修饰符**:通过反射创建的对象可以访问类中私有(private)、保护(protected)等修饰的成员
2. **配置化开发**:可与配置文件结合,动态创建对象和调用方法,需求变更时无需修改代码,仅需修改配置文件
### 1.2 反射的学习重点
反射的本质是操作**Class字节码文件对象**,核心学习内容包括:
1. 如何获取Class字节码文件对象
2. 如何通过反射获取并使用构造方法(创建对象)
3. 如何通过反射获取并操作成员变量(赋值、取值)
4. 如何通过反射获取并调用成员方法(执行方法)
### 1.3 获取Class字节码文件对象的三种方式
Class字节码文件对象是反射的入口,在JVM内存中**唯一**(一个类只有一个Class对象)。
| 获取方式 | 代码示例 | 适用场景 |
|----------|----------|----------|
| Class.forName("全类名") | `Class clazz = Class.forName("com.itheima.Student");` | 最常用,适用于配置文件加载(全类名可配置) |
| 类名.class | `Class clazz =
Student.class;` | 已知具体类,编译期确定类型 |
| 对象.getClass() | `Student s = new Student();
Class clazz = s.getClass();` | 已知对象实例,需先创建对象 |
#### 代码验证(唯一性)
```java
// 方式1:Class.forName
Class clazz1 = Class.forName("com.itheima.reflectdemo.Student");
// 方式2:类名.class
Class clazz2 = Student.class;
// 方式3:对象.getClass()
Student s = new Student();
Class clazz3 = s.getClass();
// 验证内存中唯一
System.out.println(clazz1 == clazz2);
// true
System.out.println(clazz2 == clazz3);
// true

1.4 字节码文件与字节码文件对象

  • Java文件:开发者编写的.java源代码文件
  • 字节码文件:Java文件编译后生成的.class文件(硬盘中真实存在)
  • 字节码文件对象.class文件加载到JVM内存后,虚拟机自动创建的Class对象(内存中唯一,包含类的所有信息)

1.5 通过反射获取构造方法

反射提供了4种获取构造方法的API,核心规则:

  • get:获取公开(public)成员
  • getDeclared:获取所有成员(包含私有)
  • 复数形式(Constructors):获取所有构造方法
  • 私有构造需通过setAccessible(true)临时修改访问权限(暴力反射)

方法名

说明

Constructor<?>[] getConstructors()

获取所有公开构造方法

Constructor<?>[] getDeclaredConstructors()

获取所有构造方法(含私有)

Constructor<T> getConstructor(Class<?>... parameterTypes)

获取指定公开构造方法(参数为参数类型的Class对象)

Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)

获取指定构造方法(含私有)

代码示例
public class ReflectDemo2
{
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
// 1. 获取Class对象
Class clazz = Class.forName("com.itheima.reflectdemo.Student");
// 2. 获取所有公开构造方法
Constructor[] constructors1 = clazz.getConstructors();
for (Constructor constructor : constructors1) {
System.out.println(constructor);
}
// 3. 获取所有构造方法(含私有)
System.out.println("=======================");
Constructor[] constructors2 = clazz.getDeclaredConstructors();
for (Constructor constructor : constructors2) {
System.out.println(constructor);
}
// 4. 获取指定公开构造方法
System.out.println("=======================");
Constructor con1 = clazz.getConstructor();
// 空参构造
Constructor con2 = clazz.getConstructor(String.class,
int.class)
;
// 带参构造
System.out.println(con1);
System.out.println(con2);
// 5. 获取指定私有构造方法
System.out.println("=======================");
Constructor con3 = clazz.getDeclaredConstructor(String.class)
;
// 私有单参构造
System.out.println(con3);
}
}

1.6 通过反射创建对象(构造方法的使用)

通过Constructor对象的newInstance()方法创建对象,步骤如下:

  1. 获取Class对象
  2. 获取目标构造方法(公开/私有)
  3. 私有构造需调用setAccessible(true)
  4. 调用newInstance(参数)创建对象
代码示例(Student类见下文)
public class ReflectDemo3
{
public static void main(String[] args) throws Exception {
// 1. 获取Class对象
Class clazz = Class.forName("com.itheima.reflectdemo.Student");
// 需求1:通过空参构造创建对象
System.out.println("=== 空参构造创建对象 ===");
Constructor con1 = clazz.getConstructor();
// 获取空参构造
Student stu1 = (Student) con1.newInstance();
// 创建对象
System.out.println(stu1);
// 需求2:通过私有带参构造创建对象
System.out.println("=== 私有带参构造创建对象 ===");
Constructor con2 = clazz.getDeclaredConstructor(String.class,
int.class)
;
// 获取私有构造
con2.setAccessible(true);
// 暴力反射:临时修改访问权限
Student stu2 = (Student) con2.newInstance("张三", 23);
// 创建对象
System.out.println(stu2);
}
}
// 对应的Student类
class Student
{
private String name;
private int age;
// 公开空参构造
public Student() {
}
// 私有带参构造
private Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{name='" + name + "', age=" + age + "}";
}
}

1.7 通过反射获取成员变量

获取成员变量的API规则与构造方法一致,核心类为java.lang.reflect.Field

方法名

说明

Field[] getFields()

获取所有公开成员变量

Field[] getDeclaredFields()

获取所有成员变量(含私有)

Field getField(String name)

获取指定公开成员变量(参数为变量名)

Field getDeclaredField(String name)

获取指定成员变量(含私有)

代码示例
public class ReflectDemo4
{
public static void main(String[] args) throws Exception {
// 1. 获取Class对象
Class clazz = Class.forName("com.itheima.reflectdemo.Student");
// 2. 获取所有公开成员变量
Field[] fields1 = clazz.getFields();
System.out.println("=== 所有公开成员变量 ===");
for (Field field : fields1) {
System.out.println(field);
}
// 3. 获取所有成员变量(含私有)
System.out.println("=== 所有成员变量(含私有) ===");
Field[] fields2 = clazz.getDeclaredFields();
for (Field field : fields2) {
System.out.println(field);
}
// 4. 获取指定公开成员变量
System.out.println("=== 指定公开成员变量 ===");
Field genderField = clazz.getField("gender");
System.out.println(genderField);
// 5. 获取指定私有成员变量
System.out.println("=== 指定私有成员变量 ===");
Field nameField = clazz.getDeclaredField("name");
System.out.println(nameField);
}
}
// 对应的Student类
class Student
{
private String name;
// 私有变量
private int age;
// 私有变量
public String gender;
// 公开变量
public String address;
// 公开变量
// 构造方法省略...
}

1.8 通过反射操作成员变量(赋值与取值)

通过Field对象的set()get()方法操作成员变量:

  • void set(Object obj, Object value):给obj对象的当前变量赋值为value
  • Object get(Object obj):获取obj对象的当前变量值
代码示例
public class ReflectDemo5
{
public static void main(String[] args) throws Exception {
// 1. 创建Student对象
Student s = new Student("张三", 23, "广州");
// 2. 获取Class对象
Class clazz = Class.forName("com.itheima.reflectdemo.Student");
// 3. 获取私有成员变量name
Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true);
// 暴力反射
// 4. 修改name的值
nameField.set(s, "王五");
// 给s对象的name赋值为"王五"
// 5. 获取name的值
String newName = (String) nameField.get(s);
System.out.println("修改后的name:" + newName);
System.out.println("修改后的对象:" + s);
}
}

1.9 通过反射获取成员方法

获取成员方法的API规则与前两者一致,核心类为java.lang.reflect.Method

方法名

说明

Method[] getMethods()

获取所有公开成员方法(含父类继承的公开方法)

Method[] getDeclaredMethods()

获取所有成员方法(含私有,仅当前类)

Method getMethod(String name, Class<?>... parameterTypes)

获取指定公开方法(参数1:方法名;参数2:方法参数类型的Class对象)

Method getDeclaredMethod(String name, Class<?>... parameterTypes)

获取指定方法(含私有)

代码示例
public class ReflectDemo6
{
public static void main(String[] args) throws Exception {
// 1. 获取Class对象
Class clazz = Class.forName("com.itheima.reflectdemo.Student");
// 2. 获取所有公开方法(含父类)
System.out.println("=== 所有公开方法(含父类) ===");
Method[] methods1 = clazz.getMethods();
for (Method method : methods1) {
System.out.println(method);
}
// 3. 获取所有方法(含私有,仅当前类)
System.out.println("=== 所有方法(含私有) ===");
Method[] methods2 = clazz.getDeclaredMethods();
for (Method method : methods2) {
System.out.println(method);
}
// 4. 获取指定公开方法
System.out.println("=== 指定公开方法(eat) ===");
Method eatMethod = clazz.getMethod("eat", String.class)
;
// 带参方法
System.out.println(eatMethod);
// 5. 获取指定私有方法
System.out.println("=== 指定私有方法(study) ===");
Method studyMethod = clazz.getDeclaredMethod("study");
// 空参方法
System.out.println(studyMethod);
}
}
// 对应的Student类
class Student
{
// 私有方法
private void study() {
System.out.println("学生在学习");
}
// 公开带参方法
public String eat(String food) {
System.out.println("学生在吃" + food);
return "吃饱了";
}
}

1.10 通过反射调用成员方法

通过Method对象的invoke()方法调用方法:

  • Object invoke(Object obj, Object... args):调用obj对象的当前方法,参数为args
  • 返回值:方法的返回值(无返回值则为null)
代码示例
public class ReflectDemo7
{
public static void main(String[] args) throws Exception {
// 1. 创建Student对象
Student s = new Student();
// 2. 获取Class对象
Class clazz = Class.forName("com.itheima.reflectdemo.Student");
// 3. 获取指定方法(eat方法)
Method eatMethod = clazz.getMethod("eat", String.class)
;
// 4. 调用方法
String result = (String) eatMethod.invoke(s, "重庆小面");
// 传递参数
System.out.println("方法返回值:" + result);
// 5. 调用私有方法(study方法)
Method studyMethod = clazz.getDeclaredMethod("study");
studyMethod.setAccessible(true);
// 暴力反射
studyMethod.invoke(s);
// 无参方法,无需传递args
}
}

1.11 反射面试题:反射的优缺点

优点
  1. 灵活性高:可动态创建对象和调用方法,适用于框架开发(如Spring、MyBatis)
  2. 无视修饰符:可访问私有成员,方便底层框架实现(如ORM框架操作私有字段)
  3. 配置化开发:结合配置文件,需求变更无需修改代码(如修改配置文件切换实现类)
缺点
  1. 性能损耗:反射操作需要解析字节码,性能比直接调用低(约为直接调用的1/100)
  2. 安全性问题:破坏封装性,可能导致非法访问(如修改私有字段)
  3. 代码可读性差:反射代码较繁琐,调试难度高

1.12 反射实战1:泛型擦除

核心概念

集合的泛型仅在编译期有效,编译为.class文件后,泛型信息会被擦除(即JVM运行时不识别泛型)。通过反射可绕过泛型限制,向泛型集合中添加任意类型数据。

代码示例
public class ReflectDemo8
{
public static void main(String[] args) throws Exception {
// 1. 创建泛型集合(指定Integer类型)
ArrayList<
Integer> list = new ArrayList<
>();
list.add(123);
// 正常添加Integer类型
// list.add("abc"); // 编译报错,泛型限制
// 2. 通过反射绕过泛型限制
Class clazz = list.getClass();
// 获取add方法(泛型擦除后,参数类型为Object)
Method addMethod = clazz.getMethod("add", Object.class)
;
// 调用add方法添加String类型
addMethod.invoke(list, "abc");
// 3. 打印集合(包含Integer和String)
System.out.println(list);
// 输出:[123, abc]
}
}

1.13 反射实战2:修改字符串内容

字符串不可变的本质

字符串底层由private final byte[] value数组存储,不可变的原因:

  1. private:外部无法直接访问value数组
  2. final:value数组的地址值不可修改
  3. get/set方法:外部无法操作value数组
反射修改字符串(原理)

通过反射获取value字段,修改数组内部元素(不改变数组地址值,规避final限制)。

代码示例(注意:JDK 9+已屏蔽此操作)
public class ReflectDemo9
{
public static void main(String[] args) throws Exception {
String s = "abc";
String ss = "abc";
// 与s共享同一个字符数组
// 1. 获取String的Class对象
Class clazz = s.getClass();
// 2. 获取value字段(私有final)
Field valueField = clazz.getDeclaredField("value");
valueField.setAccessible(true);
// 暴力反射
// 3. 修改value数组的内容
byte[] value = (byte[]) valueField.get(s);
value[0] = 100;
// 'a' -> 'd'
// 4. 打印结果(两个字符串均被修改)
System.out.println(s);
// 输出:dbc
System.out.println(ss);
// 输出:dbc
}
}

1.14 反射实战3:结合配置文件动态开发(重点)

需求

通过配置文件指定类名和方法名,动态创建对象并调用方法,需求变更时无需修改代码。

实现步骤
  1. 创建配置文件(prop.properties
  2. 加载配置文件,读取类名和方法名
  3. 通过反射创建对象并调用方法
代码示例
public class ReflectDemo10
{
public static void main(String[] args) throws Exception {
// 1. 加载配置文件
Properties prop = new Properties();
prop.load(new FileInputStream("prop