📖 Java 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为 Java 语言的反射机制。
# 反射机制
框架设计的灵魂(写框架需要用到反射机制,使用框架不需要会)
-
框架:半成品软件,可以在框架的基础上进行软件开发,简化编码
-
反射:将类的各个组成部分封装为其它对象,这就是反射机制
❤️好处:
- 可以在程序运行过程中,操作这些对象
- 可以解耦,提高程序的可扩展性
示例 Person
类:
package cn.itcast.domain; | |
public class Person { | |
private String name; | |
private int age; | |
public Person() { | |
} | |
public Person(String name, int age) { | |
this.name = name; | |
this.age = age; | |
} | |
public String getName() { | |
return name; | |
} | |
public void setName(String name) { | |
this.name = name; | |
} | |
public int getAge() { | |
return age; | |
} | |
public void setAge(int age) { | |
this.age = age; | |
} | |
} |
# 获取 Class 对象的方式
-
Class.forName ("全类名"):将字节码文件加载进内存,返回 Class 对象
💡:👉多用于配置文件,将类名定义在配置文件中,读取文件,加载类
-
类名.class:通过类名的属性 class 获取
💡:👉多用于参数的传递
-
对象.getClass ():getClass () 方法在 Object 类中定义着
💡:👉多用于对象的获取字节码的方式
ReflectDemo1
:
public static void main(String[] args) throws ClassNotFoundException { | |
// 1 Class.forName ("全类名") | |
Class cls1 = Class.forName("cn.itcast.domain.Person"); | |
System.out.println(cls1); | |
// 2 类名.class | |
Class cls2 = Person.class; | |
System.out.println(cls2); | |
// 3 对象.getClass () | |
Person person = new Person(); | |
Class cls3 = person.getClass(); | |
System.out.println(cls3); | |
// == 比较三个对象 | |
System.out.println(cls1 == cls2); | |
System.out.println(cls1 == cls3); | |
} |
结论:
同一个字节码文件 (*.class) 在一次程序运行过程中,只会被加载一次,无论通过哪种方式获取的 Class 对象都是同一个
# Class 对象功能
# 获取成员变量们
方法 | 定义 |
---|---|
Field[] getFields() | 获取所有 public 修饰的成员变量 |
Field getField(String name) | 获取指定名称的 public 修饰的成员变量 |
Field[] getDeclaredFields() | 获取所有的成员变量,不考虑修饰符 |
Field getDeclaredField(String name) | 获取指定成员变量,不考虑修饰符 |
Person
类添加几个不同访问权限的 Field,以及重写 toString()
方法
public class Person { | |
... | |
public String a; | |
protected String b; | |
String c; | |
private String d; | |
@Override | |
public String toString() { | |
return "Person{" + | |
"name='" + name + '\'' + | |
", age=" + age + | |
", a='" + a + '\'' + | |
", b='" + b + '\'' + | |
", c='" + c + '\'' + | |
", d='" + d + '\'' + | |
'}'; | |
} | |
} |
# getFields()
public static void main(String[] args) throws Exception { | |
// 获取 Person 的 class 对象 | |
Class personClass = Person.class; | |
Field[] fields = personClass.getFields(); | |
for (Field field : fields) { | |
System.out.println(field); | |
} | |
} |
# getField(String name)
public static void main(String[] args) throws Exception { | |
// 获取 Person 的 class 对象 | |
Class personClass = Person.class; | |
Field a = personClass.getField("a"); | |
// 获取成员变量 a 的值 | |
Person person1 = new Person(); | |
Object value1 = a.get(person1); | |
System.out.println(value1); | |
a.set(person1, "张三"); // 设置成员变量的值 | |
System.out.println(person1); | |
} |
# getDeclaredFields()
public static void main(String[] args) throws Exception { | |
// 获取 Person 的 class 对象 | |
Class personClass = Person.class; | |
Field[] declaredFields = personClass.getDeclaredFields(); | |
for (Field declaredField : declaredFields) { | |
System.out.println(declaredField); | |
} | |
} |
# getDeclaredField(String name)
public static void main(String[] args) throws Exception { | |
// 获取 Person 的 class 对象 | |
Class personClass = Person.class; | |
Field d = personClass.getDeclaredField("d"); | |
System.out.println(d); | |
// 忽略访问权限修饰符的安全检查 | |
d.setAccessible(true); // 暴力反射 | |
Person person2 = new Person(); | |
Object value2 = d.get(person2); | |
System.out.println(value2); | |
} |
# Field:成员变量
-
设置值
void set(Object obj, Object value)
-
获取值
get(Object obj)
-
忽略访问权限修饰符的安全检查
setAccessible (true):暴力反射
# 获取构造方法们
方法 | 定义 |
---|---|
Constructor<?>[] getConstructors() | 返回所有 public 修饰的构造方法 |
Constructor<T> getConstructor (类 <?>... parameterTypes) | 返回参数配型符合的 public 修饰的构造方法 |
Constructor<?>[] getDeclaredConstructors() | 返回所有构造函数,不考虑修饰符 |
Constructor<T> getDeclaredConstructor (类 <?>... parameterTypes) | 根据指定参数获取匹配的构造方法,不考虑修饰符 |
Person
类添加几个不同访问权限的构造方法
public class Person { | |
... | |
private Person(String a, String b) { | |
this.a = a; | |
this.b = b; | |
} | |
protected Person(String a, String b, String c) { | |
this.a = a; | |
this.b = b; | |
this.c = c; | |
} | |
Person(String a, String b, String c, String d) { | |
this.a = a; | |
this.b = b; | |
this.c = c; | |
this.d = d; | |
} | |
... | |
} |
方便测试添加一个 Student
类
package cn.itcast.domain; | |
public class Student { | |
private String name; | |
private int age; | |
private Student() { | |
} | |
public Student(String name) { | |
this.name = name; | |
} | |
Student(int age) { | |
this.age = age; | |
} | |
protected Student(String name, int age) { | |
this.name = name; | |
this.age = age; | |
} | |
} |
# getConstructors()
public static void main(String[] args) throws Exception { | |
// 获取 Person 的 class 对象 | |
Class personClass = Person.class; | |
Constructor[] constructors = personClass.getConstructors(); | |
for (Constructor constructor : constructors) { | |
System.out.println(constructor); | |
} | |
} |
# getConstructor (类 <?>... parameterTypes)
public static void main(String[] args) throws Exception { | |
// 获取 Person 的 class 对象 | |
Class personClass = Person.class; | |
Constructor constructor1 = personClass.getConstructor(); | |
System.out.println(constructor1); | |
Object person1 = constructor1.newInstance(); // 创建对象 | |
System.out.println(person1); | |
System.out.println("--------------------"); | |
Constructor constructor2 = personClass.getConstructor(String.class, int.class); | |
System.out.println(constructor2); | |
Object person2 = constructor2.newInstance("张伟", 23); // 创建对象 | |
System.out.println(person2); | |
} |
# getDeclaredConstructors()
public static void main(String[] args) throws Exception { | |
// 获取 Student 的 class 对象 | |
Class studentClass = Student.class; | |
Constructor[] declaredConstructors = studentClass.getDeclaredConstructors(); | |
for (Constructor declaredConstructor : declaredConstructors) { | |
System.out.println(declaredConstructor); | |
} | |
} |
# getDeclaredConstructor (类 <?>... parameterTypes)
public static void main(String[] args) throws Exception { | |
// 获取 Student 的 class 对象 | |
Class studentClass = Student.class; | |
Constructor declaredConstructor1 = studentClass.getDeclaredConstructor(); | |
System.out.println(declaredConstructor1); | |
// 忽略访问权限修饰符的安全检查 | |
declaredConstructor1.setAccessible(true); // 暴力反射 | |
Object student1 = declaredConstructor1.newInstance(); // 创建对象 | |
System.out.println(student1); | |
System.out.println("--------------------"); | |
Constructor declaredConstructor2 = studentClass.getDeclaredConstructor(String.class, int.class); | |
System.out.println(declaredConstructor2); | |
// 忽略访问权限修饰符的安全检查 | |
declaredConstructor2.setAccessible(true); // 暴力反射 | |
Object student2 = declaredConstructor2.newInstance("李四", 23); // 创建对象 | |
System.out.println(student2); | |
} |
# Constructor:构造方法
- 创建对象:
- T newInstance(Object... initargs)
- 如果使用空参数构造方法创建对象,操作可以简化:Class 对象的 newInstance 方法
# 获取成员方法们
方法 | 定义 |
---|---|
Method[] getMethods() | 获取本类以及父类或者父接口中所有的公共方法 (public 修饰) |
Method getMethod (String name,类 <?>... parameterTypes) | 返回本类以及父类或者父接口中指定名称(、参数)的 public 修饰的方法 |
Method[] getDeclaredMethods() | 返回本类的所有方法,包括私有的 (private、protected、默认以及 public) 的方法 |
Method getDeclaredMethod (String name,类 <?>... parameterTypes) | 返回本类的指定名称(、参数)的方法,不考虑修饰符 |
Person
类添加几个不同访问权限的自定义方法
public class Person { | |
... | |
public void eat() { | |
System.out.println("Eat..."); | |
} | |
public void eat(String food) { | |
System.out.println("Eat " + food); | |
} | |
private void run() { | |
System.out.println("running..."); | |
} | |
protected void run(String city) { | |
System.out.println("running in " + city); | |
} | |
} |
# getMethods()
public static void main(String[] args) throws Exception { | |
// 获取 Person 的 class 对象 | |
Class personClass = Person.class; | |
Method[] methods = personClass.getMethods(); | |
for (Method method : methods) { | |
System.out.println(method); | |
} | |
} |
# getMethod (String name,类 <?>... parameterTypes)
public static void main(String[] args) throws Exception { | |
// 获取 Person 的 class 对象 | |
Class personClass = Person.class; | |
Method eat_method1 = personClass.getMethod("eat"); | |
System.out.println(eat_method1); | |
Person person1 = new Person(); | |
eat_method1.invoke(person1); // 执行方法 | |
System.out.println("--------------------"); | |
Method eat_method2 = personClass.getMethod("eat", String.class); | |
System.out.println(eat_method2); | |
Person person2 = new Person(); | |
eat_method2.invoke(person2, "rice"); // 执行方法 | |
} |
# getDeclaredMethods()
public static void main(String[] args) throws Exception { | |
// 获取 Person 的 class 对象 | |
Class personClass = Person.class; | |
Method[] declaredMethods = personClass.getDeclaredMethods(); | |
for (Method declaredMethod : declaredMethods) { | |
declaredMethod.setAccessible(true); | |
System.out.println(declaredMethod); | |
String name = declaredMethod.getName(); // 获取方法名称 | |
System.out.println(name); | |
} | |
} |
# getDeclaredMethod (String name,类 <?>... parameterTypes)
public static void main(String[] args) throws Exception { | |
// 获取 Person 的 class 对象 | |
Class personClass = Person.class; | |
Method run_method1 = personClass.getDeclaredMethod("run"); | |
System.out.println(run_method1); | |
Person person3 = new Person(); | |
run_method1.setAccessible(true); // 暴力反射 | |
run_method1.invoke(person3); // 执行方法 | |
System.out.println("--------------------"); | |
Method run_method2 = personClass.getDeclaredMethod("run", String.class); | |
System.out.println(run_method2); | |
Person person4 = new Person(); | |
run_method2.setAccessible(true); // 暴力反射 | |
run_method2.invoke(person4, "Beijing"); // 执行方法 | |
} |
# Method:方法对象
- 执行方法:
- Object invoke(Object obj, Object... args)
- 获取方法名称:
- String getName:获取方法名
# 获取类名
- String getName()
# 案例
需求:写一个 “框架”,不能改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法实现
- 实现:
- 配置文件
- 反射
- 步骤:
- 将需要创建的对象的全类名和需要执行的方法定义在配置文件中
- 在程序中加载读取配置文件
- 使用反射技术来加载类文件进内存
- 创建对象
- 执行方法
新建 pro.properties
配置文件
className=cn.itcast.reflect.Person | |
methodName=eat |
使用反射机制创建对象并调用方法
package cn.itcast.reflect; | |
import java.io.InputStream; | |
import java.lang.reflect.Method; | |
import java.util.Properties; | |
public class ReflectTest { | |
/** | |
* 假设为框架类 | |
* */ | |
public static void main(String[] args) throws Exception { | |
// 可以创建任意类的对象,可以执行任意方法 | |
// 创建 Properties 对象 | |
Properties pro = new Properties(); | |
// 加载配置文件,转换为一个集合 | |
ClassLoader classLoader = ReflectTest.class.getClassLoader(); | |
// 获取 class 目录下的配置文件 | |
InputStream is = classLoader.getResourceAsStream("pro.properties"); | |
pro.load(is); | |
// 获取配置文件中定义的数据 | |
String className = pro.getProperty("className"); | |
String methodName = pro.getProperty("methodName"); | |
// 加载该类进内存 | |
Class cls = Class.forName(className); | |
// 创建对象 | |
Object obj = cls.newInstance(); | |
// 获取方法对象 | |
Method method = cls.getMethod(methodName); | |
// 执行方法 | |
method.invoke(obj); | |
} | |
} |
如果不想调用 Person
类的方法,可以直接修改 pro.properties
配置文件,而不修改 Java 源代码。
className=cn.itcast.reflect.Student | |
methodName=run |
当 Java 代码量庞大时,直接修改 Java 源代码,要经过重新测试、重新编译、重新上线,而仅仅修改配置文件,配置文件只是一个物理文件,改完就完事了。