《Java反射与动态代理详解:从原理到实践》
1. 反射(Reflection)
1.1 反射的概述
反射是Java语言的核心特性之一,它允许程序在运行状态下动态获取类的信息并操作类的成员(构造方法、成员变量、成员方法)。
专业定义
- 对于任意一个类,都能够知道这个类的所有属性和方法
- 对于任意一个对象,都能够调用它的任意属性和方法
- 这种动态获取信息以及动态调用对象方法的功能称为Java反射机制
通俗理解(核心)
- 无视访问修饰符:通过反射创建的对象可以访问类中私有(private)、保护(protected)等修饰的成员
- 配置化开发:可与配置文件结合,动态创建对象和调用方法,需求变更时无需修改代码,仅需修改配置文件
1.2 反射的学习重点
反射的本质是操作Class字节码文件对象,核心学习内容包括:
- 如何获取Class字节码文件对象
- 如何通过反射获取并使用构造方法(创建对象)
- 如何通过反射获取并操作成员变量(赋值、取值)
- 如何通过反射获取并调用成员方法(执行方法)
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()
方法创建对象,步骤如下:
- 获取Class对象
- 获取目标构造方法(公开/私有)
- 私有构造需调用
setAccessible(true)
- 调用
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;}@Overridepublic 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对象的当前变量赋值为valueObject 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. 获取私有成员变量nameField 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 反射面试题:反射的优缺点
优点
- 灵活性高:可动态创建对象和调用方法,适用于框架开发(如Spring、MyBatis)
- 无视修饰符:可访问私有成员,方便底层框架实现(如ORM框架操作私有字段)
- 配置化开发:结合配置文件,需求变更无需修改代码(如修改配置文件切换实现类)
缺点
- 性能损耗:反射操作需要解析字节码,性能比直接调用低(约为直接调用的1/100)
- 安全性问题:破坏封装性,可能导致非法访问(如修改私有字段)
- 代码可读性差:反射代码较繁琐,调试难度高
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
数组存储,不可变的原因:
private
:外部无法直接访问value数组final
:value数组的地址值不可修改- 无
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); // 输出:dbcSystem.out.println(ss); // 输出:dbc}
}
1.14 反射实战3:结合配置文件动态开发(重点)
需求
通过配置文件指定类名和方法名,动态创建对象并调用方法,需求变更时无需修改代码。
实现步骤
- 创建配置文件(
prop.properties
) - 加载配置文件,读取类名和方法名
- 通过反射创建对象并调用方法
代码示例
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()
方法创建对象,步骤如下:
- 获取Class对象
- 获取目标构造方法(公开/私有)
- 私有构造需调用
setAccessible(true)
- 调用
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;}@Overridepublic 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对象的当前变量赋值为valueObject 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. 获取私有成员变量nameField 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 反射面试题:反射的优缺点
优点
- 灵活性高:可动态创建对象和调用方法,适用于框架开发(如Spring、MyBatis)
- 无视修饰符:可访问私有成员,方便底层框架实现(如ORM框架操作私有字段)
- 配置化开发:结合配置文件,需求变更无需修改代码(如修改配置文件切换实现类)
缺点
- 性能损耗:反射操作需要解析字节码,性能比直接调用低(约为直接调用的1/100)
- 安全性问题:破坏封装性,可能导致非法访问(如修改私有字段)
- 代码可读性差:反射代码较繁琐,调试难度高
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
数组存储,不可变的原因:
private
:外部无法直接访问value数组final
:value数组的地址值不可修改- 无
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); // 输出:dbcSystem.out.println(ss); // 输出:dbc}
}
1.14 反射实战3:结合配置文件动态开发(重点)
需求
通过配置文件指定类名和方法名,动态创建对象并调用方法,需求变更时无需修改代码。
实现步骤
- 创建配置文件(
prop.properties
) - 加载配置文件,读取类名和方法名
- 通过反射创建对象并调用方法
代码示例
public class ReflectDemo10 {public static void main(String[] args) throws Exception {// 1. 加载配置文件Properties prop = new Properties();prop.load(new FileInputStream("prop