前言:
刚工作那会使用 java 比较实在,主要注重功能的实现,觉得反射很鸡肋;类实现的细节都是可见的,操作起来简单,还非要搞一个反射,莫名其妙;若干年过去,感慨反射真是一个伟大的东西。
1. 初识反射
反射的效果:
- 可以获取任何一个类的所有属性和方法;
- 可以修改任何一个对象的所有属性;
- 可以调用任何一个对象的所有方法;
工作中大多接触的是业务代码,亲手写反射的机会不多;但是工作中用的好多框架都可以看到反射的影子,例如:注解;框架代码要求很高的普适性,需要在屏蔽类的基础上进行操作,跟反射不谋而合。
2. Class
- Class可以说是反射能够实现的基础
- class关键字是在声明java类时使用的;而Class 是java JDK提供的一个类,完整路径为 java.lang.Class
- 对于每一种类,Java虚拟机都会初始化出一个Class类型的实例,每当我们编写并且编译一个新创建的类就会产生一个对应Class对象,并且这个Class对象会被保存在同名.class文件里。
- 当我们new一个新对象或者引用静态成员变量时,Java虚拟机(JVM)中的类加载器系统会将对应Class对象加载到JVM中,然后JVM再根据这个类型信息相关的Class对象创建我们需要实例对象或者提供静态变量的引用值。
构造器是私有的,只有JVM才可以调用这个构造函数创建Class的对象 - 每个class(注意class是小写,代表普通类)类,无论创建多少个实例对象,在JVM中都对应同一个Class对象。
3. 获取 Class 三种方式
这里以 Person 类为基础进行反射的操作:
package anno;
public class Person {
public String name;
private int age;
public Person(String name, int age) {
this.age = age;
this.name = name;
System.out.println("new a person, name is " + name + ", age is " + age);
}
@Override
public String toString() {
return "name: " + name + ", age: " + age;
}
}
获取 Class 的三种方式如下:
Class p1 = Person.class;
Class p2 = Class.forName("anno.Person");
Person person = new Person("jonny", 20);
Class p3 = person.getClass();
4. 操作属性
1)获取属性
方法有四个,如下:
//获取指定的变量(只要是声明的变量都能获得,包括private)
getDeclaredField(String name)
//获取指定的变量(只能获得public的)
getField(String name)
//获取所有声明的变量(包括private)
getDeclaredFields()
//获取所有的public变量
getFields()
使用举例:
Person person = new Person("jonny", 20);
Class p3 = person.getClass();
Field[] fields = p3.getDeclaredFields();
System.out.println("*******反射获取类的字段以及字段相关信息*******");
for (Field field : fields) {
System.out.println("属性字段:" + field.getName() + ", 类型:" + field.getType() + ",修饰符:" + Modifier.toString(field.getModifiers()));
}
打印结果如下:
new a person, name is jonny, age is 20
*******反射获取类的字段以及字段相关信息*******
属性字段:name, 类型:class java.lang.String,修饰符:public
属性字段:age, 类型:int,修饰符:private
2)操作属性
Person person = new Person("jonny", 20);
Class p3 = person.getClass();
try {
System.out.println("*******反射获取类public字段的值并修改*******");
Field fieldName = p3.getField("name");
String name = (String) fieldName.get(person);
System.out.println("从反射获取的name值:" + name);
fieldName.set(person,"jenny");
System.out.println(person.toString());
System.out.println("*******反射获取类private字段的值并修改*******");
Field fieldAge = p3.getDeclaredField("age");
fieldAge.setAccessible(true); // 私有属性操作需要
int age = (int) fieldAge.get(person);
System.out.println("从反射获取的age值:" + age);
fieldAge.set(person,18);
System.out.println(person.toString());
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
打印结果如下:
new a person, name is jonny, age is 20
*******反射获取类public字段的值并修改*******
从反射获取的name值:jonny
name: jenny, age: 20
*******反射获取类private字段的值并修改*******
从反射获取的age值:20
name: jenny, age: 18
5. 操作构造方法
1)获取构造方法
//获取指定构造函数,参数parameterTypes为构造方法的参数类型
getDeclaredConstructor(Class<?>... parameterTypes)
//获取指定public构造函数,参数parameterTypes为构造方法的参数类型
getConstructor(Class<?>... parameterTypes)
//获取所有声明的构造方法
getDeclaredConstructors()
//获取所有的public构造方法
getConstructors()
Class p2 = Class.forName("anno.Person");
Constructor[] constructors = p2.getDeclaredConstructors();
System.out.println("构造方法数量:" + constructors.length);
for (Constructor constructor : constructors) {
System.out.print("构造函数修饰符:" + Modifier.toString(constructor.getModifiers()) + ", 参数:");
Class[] parameterTypes = constructor.getParameterTypes();
for (Class parameterType : parameterTypes) {
System.out.print(parameterType.getName() + ",");
}
}
打印结果如下:
new a person, name is jonny, age is 20
构造方法数量:1
构造函数修饰符:public, 参数:java.lang.String,int,
2)用构造方法获取实例
Class p2 = Class.forName("anno.Person");
Class[] parameterArr = {String.class, int.class};
Constructor constructor = p2.getDeclaredConstructor(parameterArr);
Person newPerson = (Person) constructor.newInstance("jenny", 19);
打印结果如下:
new a person, name is jenny, age is 19
6.操作方法
1)相关方法
//根据方法名获得指定的方法,参数parameterTypes为方法的参数类型
getDeclaredMethod(String name, Class<?>... parameterTypes)
//根据方法名获取指定的public方法
getMethod(String name, Class<?>... parameterTypes)
//获取所有声明的方法
getDeclaredMethods()
//获取所有的public方法
getMethods()
//获取目标方法返回类型对应的Class对象
getReturnType()或者 getGenericReturnType()
// 执行方法
invoke()
2)执行方法
Person person = new Person("jonny", 20);
Class p3 = person.getClass();
Class[] parameterArr = {};
Method method = p3.getDeclaredMethod("toString",parameterArr);
String resultStr = (String) method.invoke(person, parameterArr);
System.out.println(resultStr);
打印结果如下:
new a person, name is jonny, age is 20
name: jonny, age: 20
7.注解
注解这块单独写