java 反射的定义:主要是指程序可以访问、检测和修改它本身状态或行为的一种能力。简单来说,就是一面镜子,让你清楚的看到自己的所有细节。
1. 为什么要使用反射?
- 问题来了,反射破坏了封装性,甚至让私有变量都可以被外部访问到,使得类变得不那么安全了。为什么还要有反射呢?
- 1、 反射让开发人员可以通过外部类的全路径名创建对象,并使用这些类,实现一些扩展的功能。如Spring Aop功能。
- 2、反射让开发人员可以枚举出类的全部成员,包括构造函数、属性、方法。以帮助开发者写出正确的代码。
- 3、测试时可以利用反射 API 访问类的私有成员,以保证测试代码覆盖率。
- 总的来说,就是程序员可以利用反射拿到类的信息,进行正常情况下无法进行的扩展与测试。
2. 怎么使用 反射?
- 在使用Java反射机制时,主要步骤包括:
- 1.获取 目标类型的Class对象
- 2.通过 Class 对象分别获取Constructor类对象、Method类对象 & Field 类对象
- 3.通过步骤2获取的对象分别获取类的构造函数、方法&属性的具体信息,并进行后续操作
2.1 获取 Class 对象的三种方式
- 三种方式获取的 Class 都是同一个
public class Person {
private int privateNum = 0;
int num = 0;
protected int protectedNum = 0;
public int publicNum = 0;
private int privateNum() {
return privateNum;
}
int num() {
return num;
}
protected int protectedNum() {
return protectedNum;
}
public int publicNum() {
return publicNum;
}
public Person() {
}
public Person(int privateNum) {
this.privateNum = privateNum;
}
private Person(int privateNum, int num) {
this.privateNum = privateNum;
this.num = num;
}
public Person(int privateNum, int num, int protectedNum) {
this.privateNum = privateNum;
this.num = num;
this.protectedNum = protectedNum;
}
private Person(int privateNum, int num, int protectedNum, int publicNum) {
this.privateNum = privateNum;
this.num = num;
this.protectedNum = protectedNum;
this.publicNum = publicNum;
}
}
2.2 根据 class 获取Constructor、Method、Field 对象
- Person类
public class Person {
private int privateNum = 0;
int num = 0;
protected int protectedNum = 0;
public int publicNum = 0;
}
2.2.1 获取 Field 对象
import java.lang.reflect.Field;
public class GetFieldTest {
public static void main(String[] args) {
/* 1. 获取 Person 的 class 对象 */
Person person = new Person();
Class aClass = person.getClass();
/* 获取公有字段: 只有public字段可以获取到 */
// 获取单个公有字段
try {
Field publicNum = aClass.getField("publicNum");
System.out.println("按 name 获取的 Field 对象 --> " + publicNum);
/* 找不到报错 NoSuchFieldException */
Field privateNum = aClass.getField("privateNum");
System.out.println("按 name 获取的 Field 对象 --> " + privateNum);
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
// 获取所有公有字段
Field[] fields = aClass.getFields();
System.out.println("======= 输出全部公有字段 ======");
for (Field field : fields) {
System.out.println(field);
}
System.out.println("============= 华丽分割线 ===========");
// 获取单个声明字段
try {
Field publicNum = aClass.getDeclaredField("privateNum");
System.out.println("按 name 获取的 DeclaredField 对象 --> " + publicNum);
/* 找不到报错 NoSuchFieldException */
Field privateNum = aClass.getDeclaredField("privateNum01");
System.out.println("按 name 获取的 DeclaredField 对象 --> " + privateNum);
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
/* 获取所有声明字段:获取所有声明的字段 */
System.out.println("======= 输出所有字段 ======");
Field[] declaredFields = aClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
}
}
2.2.2 获取 Method对象
import java.lang.reflect.Method;
public class GetMethodTest {
static class Student{}
public static void main(String[] args) {
/* 1. 获取 Person 的 class 对象 */
Person person = new Person();
Class aClass = person.getClass();
/* 获取单个和全部公有方法(public) */
try {
Method publicNum = aClass.getMethod("publicNum");
System.out.println("获取单个公有方法 publicNum : " + publicNum);
Method privateNum = aClass.getMethod("privateNum");
System.out.println("获取单个公有方法 privateNum : " + privateNum);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
Method[] methods = aClass.getMethods();
/* 全部公有方法,包括父类 */
for (Method method : methods) {
System.out.println(method);
}
System.out.println("================= 华丽分割线 ================");
/* 获取单个和全部声明方法 */
try {
Method privateNum = aClass.getDeclaredMethod("privateNum");
System.out.println("获取单个声明方法 publicNum : " + privateNum);
Method privateNum01 = aClass.getDeclaredMethod("privateNum01");
System.out.println("获取单个声明方法 privateNum : " + privateNum01);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
Method[] declaredMethods = aClass.getDeclaredMethods();
/* 全部声明方法,只有自身方法(不包括父类) */
for (Method method : declaredMethods) {
System.out.println(method);
}
System.out.println("=============== 再次华丽分割线 ===============");
/* getEnclosingMethod 方法的返回类型为Method
* 1. 当此类是本地或匿名类时,它将返回基础类的最近封闭方法。
* 2. 如果此类不是本地的或匿名的,则返回null。
*/
/* 模拟一个本地类 和 匿名类 */
class A{};
Method local = (new A().getClass()).getEnclosingMethod();
System.out.println("本地类 --> " + local);
Method anonymous = new Student(){}.getClass().getEnclosingMethod();
System.out.println("内部类 --> " + anonymous);
System.out.println("普通类 -->" + aClass.getEnclosingMethod());
}
}
2.2.3 获取 Constructor 对象
import java.lang.reflect.Constructor;
public class GetConstructorTest {
public static class Outter {
//Outter的无参数构造器
public Outter() {
//构造中定义的内部类
class Inner {
}
Class<Inner> innerClass = Inner.class;
Constructor<?> enclosingConstructor = innerClass.getEnclosingConstructor();
System.out.println("构造函数中的内部类 -- > " + enclosingConstructor);
Constructor<?> enclosingConstructor1 = new Object() {}.getClass().getEnclosingConstructor();
System.out.println("构造函数中的匿名类 -- >" + enclosingConstructor1);
}
}
public static void main(String[] args) {
/* 1. 获取 Person 的 class 对象 */
Person person = new Person();
Class aClass = person.getClass();
/* 老规矩,公有构造函数 */
try {
Constructor constructor = aClass.getConstructor();
System.out.println("单个 Integer 类型的构造函数 --> " + constructor);
Constructor constructor01 = aClass.getConstructor(int.class, int.class);
System.out.println("两个 Integer 类型的构造函数 --> " + constructor01);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
Constructor[] constructors = aClass.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
System.out.println("================ 华丽分割线 =================");
/* 老规矩,声明构造函数 */
try {
Constructor declaredConstructor = aClass.getDeclaredConstructor(int.class,int.class);
System.out.println("两个 Integer 类型的构造函数 --> " + declaredConstructor);
Constructor declaredConstructor01 = aClass.getDeclaredConstructor(int.class,String.class);
System.out.println("Integer,String 类型的构造函数 --> " + declaredConstructor);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
Constructor[] declaredConstructors = aClass.getDeclaredConstructors();
for (Constructor constructor : declaredConstructors) {
System.out.println(constructor);
}
System.out.println("============== 我是最美的分割线 ================");
System.out.println("Outter 构造器中的内部类和匿名类");
new Outter();
}
}
2.3 反射实践
- Student 类
package cn.cerish.reflection;
public class Student {
private int age;
public String name;
public int getAge() {
System.out.println("我调用了getAge()");
return age;
}
public void setAge(int age) {
System.out.println("我调用了setAge()");
this.age = age;
}
public String getName() {
System.out.println("我调用了getName()");
return name;
}
public void setName(String name) {
System.out.println("我调用了setName()");
this.name = name;
}
public Student() {
System.out.println("我是 Student 的无参构造函数");
}
public Student(int age, String name) {
this.age = age;
this.name = name;
System.out.println("我是 Student 的有参构造函数");
}
@Override
public String toString() {
return "Student{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
2.3.1 属性赋值
import java.lang.reflect.Field;
public class SetFieldTest {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InstantiationException {
/* 1. 获取 Class 对象 */
Student student = new Student();
Class<? extends Student> studentClass = student.getClass();
/* 2. 获取 Student 实例 */
Student newInstance = studentClass.newInstance();
System.out.println("初始化后的 Student --> " + newInstance);
/* 3. 获取 Field */
Field fieldAge = studentClass.getDeclaredField("age");
Field fieldName = studentClass.getDeclaredField("name");
/* 4. 私有属性必须设置,不然不能更改 */
fieldAge.setAccessible(true);
/* 5. 给实例newInstance 设置值 */
fieldAge.set(newInstance, 18);
fieldName.set(newInstance, "cerish");
/* 6. 获取实例对象并查看结果 */
System.out.println("属性赋值后的 Student --> " + newInstance);
}
}
- 若步骤 4 没有进行设置,那么运行报错如下
Exception in thread "main" java.lang.IllegalAccessException: Class cn.cerish.reflection.SetFieldTest can not access a member of class cn.cerish.reflection.Student with modifiers "private"
at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:296)
at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:288)
at java.lang.reflect.Field.set(Field.java:761)
at cn.cerish.reflection.SetFieldTest.main(SetFieldTest.java:19)
2.3.2 反射调用方法
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class InvokeMethodTest {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
/* 1. 获取 Class 对象 */
Class<? extends Student> studentClass = Student.class;
/* 2. 获取 Student 实例*/
Student newInstance = studentClass.newInstance();
/* 3. 获取 Method 调用方法 */
Method setAge = studentClass.getDeclaredMethod("setAge", int.class);
// 通过 Method对象调用 setAge(): 需传入创建的实例 & 参数
setAge.invoke(newInstance, 18);
System.out.println("调用 setAge 方法后的 newInstance --> " + newInstance);
}
}
2.3.3 反射调用有参构造函数
import java.lang.reflect.InvocationTargetException;
public class InvokeConstructorTest {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
/* 1. 获取 Class 对象 */
Class<? extends Student> studentClass = Student.class;
/* 2. 获取 Constructor 调用构造 */
Student emptyNewInstance = studentClass.getConstructor().newInstance();
System.out.println("空构造函数的 instance -->" + emptyNewInstance);
Student fullNewInstance = studentClass.getConstructor(int.class, String.class).newInstance(18, "cerish");
System.out.println("有参构造函数的 instance -->" + fullNewInstance);
}
}
3. 总结
- 反射其实就是在运行时, 获取 类Class 获取到 属性、方法、构造器等对象信息,然后对获取到的对象信息进行操作,完成扩展的功能。