反射
概述
能够分析类能力的程序叫做反射。反射可以知道一个类的所有信息,包括成员变量、成员方法、构造方法;也可以使用这些信息。
动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。
反射机制可以用来:
- 能够分析类的能力
- 在运行时查看对象
- 实现通用的数组操作代码
- 也可以无视修饰符调用类里边的内容
需要注意是我们获取和操作类的信息是通过字节码文件(.class)来操作的,而不是通过普通的java文件。因此我们必须牢记获取类的Class对象的方式。
获取字节码文件对象的三种方式
- Class这个类里面的静态方法,即
Class.forName("包名+类名")
(最常用) - 通过class属性获取,即
类.class
- 通过对象获取字节码文件对象,即
对象.getClass()
代码实现:
Demo1.class:
package TestReflect;
public class Demo1 {
/**
* 获取Class对象的三种方式
*/
public static void main(String[] args) throws ClassNotFoundException {
// 1. 通过Class.forName("包名+类名")的方式获取,需要抛出异常
Class studentClass1 = Class.forName("TestReflect.Student");
System.out.println(studentClass1);
// 2. 类名.class获取
Class studentClass2 = Student.class;
System.out.println(studentClass1 == studentClass2);
// 3. 对象.getClass()获取
Student student = new Student();
Class studentClass3 = student.getClass();
System.out.println(studentClass2 == studentClass3);
}
}
Student.java
package TestReflect;
public class Student {
private String name;
private int 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;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
运行结果:
可以看到,三种方式获得的Class对象是同一个对象。
利用反射分析类的构造方法
方法名 | 说明 |
---|---|
Constructor<?>[] getConstructors() | 获得所有public的构造(只能public修饰) |
Constructor<?>[] getDeclaredConstructors() | 获得所有的构造(包含private修饰) |
Constructor getConstructor(Class<?>… parameterTypes) | 获取指定public构造(只能public修饰) |
Constructor getDeclaredConstructor(Class<?>… parameterTypes) | 获取指定构造(包含private修饰) |
代码示例:
student.java
package TestReflect.Demo2;
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name) {
this.name = name;
}
protected Student(int age) {
this.age = age;
}
private Student(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;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
TestDemo2.java:
package TestReflect.Demo2;
import TestReflect.Demo1.Student;
import java.lang.reflect.Constructor;
public class TestDemo2 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
// 加载类对象
Class clazz = Class.forName("TestReflect.Demo2.Student");
/**
* 获取所有的public构造方法
* Constructor<?>[] getConstructors()
*/
System.out.println("=====获取所有的public构造方法=====");
Constructor[] cons = clazz.getConstructors();
for (Constructor con : cons) {
System.out.println(con);
}
System.out.println("=================================");
/**
* 获取所有的构方法
* Constructor<?>[] getDeclaredConstructors()
*/
System.out.println("=====获取所有的public构造方法=====");
Constructor[] dcons = clazz.getDeclaredConstructors();
for (Constructor dcon : dcons) {
System.out.println(dcon);
}
System.out.println("=================================");
/**
* 获取指定的public构造方法
* Constructor<T> getConstructor(Class<?>... parameterTypes)
*/
System.out.println("=====获取指定的构造方法=====");
Constructor con1 = clazz.getConstructor(String.class);
System.out.println(con1);
// 这个会报错,getConstructor无法获取私有的
// Constructor con2 = clazz.getConstructor(String.class, int.class);
// System.out.println(con2);
Constructor con2 = clazz.getDeclaredConstructor(String.class, int.class);
System.out.println(con2);
System.out.println("=================================");
}
}
运行结果:
获取构造方法并创建对象
使用newInstance创建对象。
代码实现:
Constructor con = clazz.getConstructor(String.class);
Student stu = (Student) con.newInstance("zhangsan");
System.out.println(stu);
/**
* 反射获取到的方法式一个私有方法,如果直接创建对象会报错with modifiers "private"
* 在创建对象之前执行con2.setAccessible(true);可以暂时解除访问权限控制
*/
Constructor con2 = clazz.getDeclaredConstructor(String.class, int.class);
con2.setAccessible(true);
Student stu2 = (Student) con2.newInstance("zhangsan", 18);
System.out.println(stu2);
运行结果:
利用反射分析成员变量
方法名 | 说明 |
---|---|
Field[] getFields() | 返回所有成员变量对象的数组(只能拿public的) |
Field[] getDeclaredFields() | 返回所有成员变量对象的数组,存在就能拿到 |
Field getField(String name) | 返回单个成员变量对象(只能拿public的) |
Field getDeclaredField(String name) | 返回单个成员变量对象,存在就能拿到 |
参数是成名变量名 |
获取成员变量并获取值和修改值
方法 | 说明 |
---|---|
void set(Object obj, Object value) | 赋值 |
Object get(Object obj) | 获取值 |
obj都是需要操作的对象;
如果是私有的成员变量,同样需要在获取或修改前执行f.setAccessible(true)
来暂时取消访问权限控制。
简单示例:
Student stu = new Student();
stu.setName("zhangsan");
// 注意name是私有的,所以不能使用getField
Field f = clazz.getDeclaredField("name");
// 由于是私有的,必须加这个
f.setAccessible(true);
String fname = (String) f.get(stu);
System.out.println(fname);
f.set(stu, "lisi");
fname = (String) f.get(stu);
System.out.println(fname);
运行结果:
利用反射分析成员方法
方法名 | 说明 |
---|---|
Method[] getMethods() | 返回所有成员方法对象的数组(只能拿public的) |
Method[] getDeclaredMethods() | 返回所有成员方法对象的数组,存在就能拿到 |
Method getMethod(String name, Class<?>… parameterTypes) | 返回单个成员方法对象(只能拿public的) |
Method getDeclaredMethod(String name, Class<?>… parameterTypes) | 返回单个成员方法对象,存在就能拿到 |
获取成员方法并运行
通过Object invoke(Object obj, Object... args)
:运行方法
- 参数一:用obj对象调用该方法
- 参数二:调用方法的传递的参数(如果没有就不写)
- 返回值:方法的返回值(如果没有就不写)
例如:
Student stu = new Student();
Method m = clazz.getMethod("setName", String.class);
m.invoke(stu, "zhangsan");
System.out.println(stu.getName());
运行结果