1. 概念
Java中的反射机制是指在运行时动态获取并操作类的信息的能力。通过反射,可以在运行时访问和操作对象、类、接口、方法、属性、构造函数等元素,包括私有的和受保护的元素。
2. 与反射相关的类
| 类名 | 用途 |
|---|---|
| Class类 | 代表类的实体,在运行的Java应用程序中表示类和接口 |
| Field类 | 代表类的成员变量/类的属性 |
| Method类 | 代表类的方法 |
| Constructor类 | 代表类的构造方法 |
3. Class 类的运用
java文件被编译后,生成了.class文件,JVM此时就要去解读.class文件,被编译后的.class文件也被JVM解析为一个对象,这个对象就是 java.lang.Class 。这样当程序在运行时,每个java文件就最终变成了Class类对象的一个实例。我们通过Java的反射机制应用到这个实例,就可以去获得甚至去添加改变这个类的属性和动作,使得这个类成为一个动态的类。
3.1 获得 Class 对象的三种方式
在Java中,可以通过以下三种方式获得Class对象:
- 使用对象的getClass()方法。
- 使用类的class属性。
- 使用Class类的forName()方法。
(下面的 ?为通配符)
class Student{
....
}
public class Main {
public static void main(String[] args) {
Student student = new Student();
//通过 getClass() 的方法
Class<?> c1 = student.getClass();
//通过 .class 的方式
Class<?> c2 = Student.class;
//通过 forName("这里是类的相对路径") 方法,这种方法要加异常
Class<?> c3 = null;
try {
c3 = Class.forName("demo.Student");
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
//一个类在 JVM 中只会有一个 Class 实例,即我们对上面获取的
//这三种方式获取的 class 对象都是同一个
System.out.println(c1.equals(c2)); //true
System.out.println(c1.equals(c3)); //true
System.out.println(c2.equals(c3)); //true
}
}
3.2 Class 类中的常见方法
获得类相关信息的方法:
| 方法 | 用途 |
|---|---|
| ClassLoader getClassLoader() | 获得类的加载器 |
| Class<?>[] getDeclaredClasses() | 返回一个数组,数组中包含该类中所有类和接口类的对象(包括私有的) |
| Class<?> forName(String className) | 根据类名返回类的对象 |
| Object newInstance() | 创建类的实例 |
| String getName() | 获得类的完整路径名字 |
获得类中构造器相关的方法:
| 返回类型 | 方法 | 用途 |
|---|---|---|
public Constructor<T> | getConstructor(Class...<?> parameterTypes) | 获得该类中与参数类型匹配的公有构造方法 |
public Constructor<?>[] | getConstructors() | 获得该类的所有公有构造方法 |
public Constructor<T> | getDeclaredConstructor(Class...<?> parameterTypes) | 获得该类中与参数类型匹配的构造方法(包括私有的) |
public Constructor<?>[] | getDeclaredConstructors() | 获得该类所有构造方法(包括私有的) |
常用获得类中属性相关的方法:
| 返回类型 | 方法 | 用途 |
|---|---|---|
| public Field | getField(String name) | 获得某个公有的属性对象 |
| public Field[] | getFields() | 获得所有公有的属性对象 |
| public Field | getDeclaredField(String name) | 获得某个属性对象(包括私有的) |
| public Field[] | getDeclaredFields() | 获得所有属性对象(包括私有的) |
获得类中方法相关的方法:
| 返回类型 | 方法 | 用途 |
|---|---|---|
| public Method | getMethod(String name, Class<?>... parameterTypes) | 获得该类某个公有的方法 |
| public Method[] | getMethods() | 获得该类所有公有的方法 |
| public Method | getDeclaredMethod(String name, Class<?>... parameterTypes) | 获得该类某个方法(包括私有的) |
| public Method[] | getDeclaredMethods() | 获得该类所有方法(包括私有的) |
获得类中注解相关的方法:
| 返回类型 | 方法 | 用途 |
|---|---|---|
public <T extends Annotation> T | getAnnotation(Class annotationClass) | 返回该类中与参数类型匹配的公有注解对象 |
| public Annotation[] | getAnnotations() | 返回该类所有的公有注解对象 |
public <T extends Annotation> T | getDeclaredAnnotation(Class annotationClass) | 返回该类中与参数类型匹配的所有注解对象 |
public Annotation[] | getDeclaredAnnotations() | 返回该类所有的注解对象 |
3.3 Field 类提供的常用方法
| 返回类型 | 方法名称 | 描述 |
|---|---|---|
Object | get(Object obj) | 返回指定对象上此 Field 表示的字段的值 |
void | set(Object obj, Object value) | 将指定对象上此 Field 表示的字段设置为指定的新值 |
Class<?> | getDeclaringClass() | 返回一个描述此 Field 所属类或接口的 Class 对象 |
String | getName() | 返回 Field 的名称 |
Class<?> | getType() | 返回 Field 的类型 |
int | getModifiers() | 返回此 Field 的 Java 语言修饰符 |
boolean | isAccessible() | 返回此 Field 的可访问标志 |
void | setAccessible(boolean flag) | 将此 Field 的可访问标志设置为指定的布尔值 |
3.4 Method 类提供的常用方法
| 返回类型 | 方法名称 | 描述 |
|---|---|---|
Object | invoke(Object obj, Object... args) | 以指定的参数调用此方法,并返回此方法的返回值 |
boolean | isBridge() | 如果此方法为桥方法,则返回 true,否则返回 false |
boolean | isVarArgs() | 如果此方法是使用可变数量的参数调用的,则返回 true,否则返回 false |
Class<?> | getDeclaringClass() | 返回一个描述此方法所属类或接口的 Class 对象 |
String | getName() | 返回方法的名称 |
Class<?>[] | getParameterTypes() | 返回一个 Class 对象数组,其中包含按声明顺序标识形参类型的类或接口 |
int | getParameterCount() | 返回方法的形参个数 |
Class<?> | getReturnType() | 返回方法的返回类型 |
int | getModifiers() | 返回此方法的 Java 语言修饰符 |
boolean | isAccessible() | 返回方法的可访问标志 |
void | setAccessible(boolean flag) | 将此方法的可访问标志设置为指定的布尔值 |
3.5 Constructor 类提供的常用方法
| 返回类型 | 方法名称 | 描述 |
|---|---|---|
Constructor<T> | Constructor() | 创建一个新的对象 |
T | newInstance() | 创建一个新的对象实例 |
Class<?> | getDeclaringClass() | 返回描述此构造函数所表示的类的 Class 对象 |
String | getName() | 返回构造函数的名称 |
int | getParameterCount() | 返回构造函数的参数个数 |
Class<?>[] | getParameterTypes() | 返回一个 Class 对象数组,其中包含按声明顺序标识形参类型的类或接口 |
int | getModifiers() | 返回此构造函数的 Java 语言修饰符 |
boolean | isAccessible() | 返回构造函数的可访问标志 |
void | setAccessible(boolean flag) | 将此构造函数的可访问标志设置为指定的布尔值 |
T | newInstance(Object... initargs) | 通过使用指定的初始化参数创建新实例 |
3.6 简单的案例
假设现在有如下的 Student 的类:
public class Student {
public String name;
private int id;
public int age;
//无参构造方法
public Student() {
}
//私有构造方法
private Student(String name, int id, int age) {
this.name = name;
this.id = id;
this.age = age;
}
//共有方法
public void pubMethod1() {
System.out.println("public 的方法");
}
//私有方法
private void priMethod() {
System.out.println("private 的方法");
}
//有参数的方法
private void paramMethod(String str) {
System.out.println(str);
}
//有返回值的方法
@Override
public String toString() {
return "demo.Student{" +"name=" + name +", id=" + id +", age=" + age +'}';
}
}
(1)通过反射来创建实例
// 通过反射来创建对象
public static void reflectNewInstance() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
//获取一个 Class 对象
Class<?> c1 = Class.forName("demo.Student");
//创建一个实例
Student student = (Student)c1.newInstance();
student.name = "张三";
student.age = 19;
System.out.println("获取的Student对象:"+student.toString());
}
调用该方法后的结果:
获取的Student对象:demo.Student{name=张三, id=0, age=19}
(2)通过反射获取构造方法来创建实例
// 反射私有的构造方法
public static void reflectPrivateConstructor() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//获取一个 Class 对象
Class<?> c = Class.forName("demo.Student");
//getDeclaredConstructor 的参数要对应 构造方法里的参数
Constructor<?> constructor = c.getDeclaredConstructor(String.class,int.class,int.class);
//设置访问,当参数为 true 的时候表示可以获取到私有构造方法
constructor.setAccessible(true);
//创建一个实例
Student student = (Student) constructor.newInstance("小明",1,20);
System.out.println(student);
}
调用该方法后的结果:
demo.Student{name=小明, id=1, age=20}
(3)反射私有属性
// 反射私有属性
public static void reflectPrivateField() throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException {
Class<?> c = Class.forName("demo.Student");
//反射私有属性,
Field field = c.getDeclaredField("id");
field.setAccessible(true);
//创建一个实例
Student student = (Student) c.newInstance();
field.set(student,20);
System.out.println(student);
}
结果:
demo.Student{name=null, id=20, age=0}
(4)反射私有方法
// 反射私有方法
public static void reflectPrivateMethod() throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
Class<?> c = Class.forName("demo.Student");
//获取私有方法 paramMethod
Method method = c.getDeclaredMethod("paramMethod",String.class);
method.setAccessible(true);
//创建一个实例
Student student = (Student)c.newInstance();
//调用方法
method.invoke(student,"我是 paramMethod() 方法!");
}
结果:
我是 paramMethod() 方法!