反射体系是 Java 体系中一个非常具有代表性的体系。什么是反射体系呢?
通俗来讲,反射就是对象的反向处理操作。根据类的实例化对象来反向推出类的组成。一般来说,我们采用实例化的方式是className objectName = new constructor ( ) 这就是正向的一次操作,而反向操作是什么呢?
class Student {
Student() {}
}
Student student = new Student(); // 正向操作
Class<?> cls = Student.class; // 反向操作
这里注意到,cls
的类型是一个 Class >
在这里有必要说一下 Class 与 class 的区别了。
class 是一个关键字,而 Class 则是一个类,而 Class 这个类描述的是类。也就说说 Class 是类的类(毕竟我是大写 C,比你 class 大一些)。
有三种方式可以拿到一个类的 Class 对象。
当我们拿到一个类的 Class 对象后,就可以根据这个对象来反推类的组成了。
这个方法就是在我们取得了 Class 对象之后,实例化类的一个方法。
@CallerSensitive
public T newInstance()
throws InstantiationException, IllegalAccessException
此方法的返回值类型为 T ,一般情况下我们采用 Object 来接收。并且在某些情况下,我们可以把返回值类型强转成其它的类型。这个方法实例化的对象,默认调的是无参的构造函数,如果在 Class 对象中有其它构造方法,这个方法就会失效,我们就要获取该 Class 对象的构造方法然后才能够实例化出对象。
所有的类都是有父类的。当我们拿到 Class 类对象之后就可以获得其实现的接口以及继承的父类。
interface Brother {}
interface Consin {}
class Father {}
class SonClass extends Father implements Brother, Consin {}
public class Test {
public static void main(String[] args) {
Class<?> cls = SonClass.class;
System.out.println(cls.getSuperclass());
Class<?>[] classes = cls.getInterfaces(); // 这里接口是可以有多个,所以用一个数组来存放
for (Class<?> c : classes) {
System.out.println(c);
}
}
}
一个类中的构造方法可以重载很多,所以我们要获得类的构造方法,需要 Class 类中的一些方法。
getDeclareConstructor(Class<?>... parameterTypes);
这个方法可以取得指定参数的,任何权限的构造方法。这个方法的参数都是 Class 对象,所以参数应该是:类型名.class。指的是该类型的对象。
getConstructors();
这个方法的返回值是一个 Constructor> 的数组,它可以把一个类的所有权限为 public 的构造方法存放到一个数组内返回。无法返回父类的构造方法。
getDeclaredConstructors();
这个方法与上个差不多,唯一的区别就是它能够把类中所有的构造方法全部返回,无视权限。同样无法返回父类的构造方法。
当我们拿到构造方法的时候,就能够创建实例化对象了,这个时候就可以利用 newInstance 进行实例化了。
import java.lang.reflect.Constructor;
class Person {
public int name;
public Person() {}
public Person(int name) {
this.name = name;
}
}
Class Student extends Person {
private int classNum;
private String grade;
public Student() {}
private Student(int classNum, String grade) {
this.classNum = classNum;
this.grade = grade;
}
}
public class Main {
public static void main(String[] args) {
Class cls = Student.class;
Constructor con = cls.getDeclaredConstructor(int.class, String.class);
Student stu = con.newInstance(100, "2");
Constructor[] cons = cls.getConstructors();
for (Constructor c : cons) {
System.out.println(c);
}
cons = cls.getDeclaredConstructors();
for (Constructor c : cons) {
System.out.println(c);
}
}
}
在获取普通方法时用的是 Method 类,用它来描述类中的普通方法。
该方法返回的就是一个 Method 数组,存放了本类以及父类中的所有 public 普通方法,该方法优先返回本类中的方法,也就是说数组前面的元素是本类的普通方法,后面的是父类中的普通方法。
该方法与 getMethods 差不多,但是这个方法仅仅只查询本类内部的普通方法,无视权限。
该方法会根据方法名以及参数列表获取到指定的普通方法,权限为 public ,如果指定的普通方法在本类中没有,则会去父类中查找。
该方法只在本类中查找指定名称和参数的普通方法,不在父类中查找,无关权限。
invoke(Object object, Object...);
method.invoke(类对象,方法的参数列表);
调用获取到的普通方法的时候必须要有类的实例化对象,如果调用的普通方法没有参数,那么就不需要写参数列表了。
当我们获取到 private 的方法的时候,是否可以调用呢?
void setAccessible(boolean flag);
我们获取到的 private 方法是不能够直接调用的,而 setAccessible 就是帮助我们调用。
这个方法的参数是一个 Boolean 类型的,当我们需要把普通方法或者属性破坏掉的时候,传入 true 即可。这个方法不会将 private 转化成 public ,权限并没有改变,只不过是对外可见了,同样也可以将 public 转化为 private ,只需要传入 false 即可。
import java.lang.reflect.Method;
class Person {
public Person() {}
public void PersonMethod1(String str) {
System.out.println(str);
}
private void PersonMethod2() {}
}
class Student extends Person {
public Student() {}
public void StudentMethod1() {}
private void StudentMethod2(String str) {
System.out.pringln(str);
}
}
public class Main {
public static void main(String[] args) {
Class<?> cls = Student.class;
Method[] methods = cls.getMethods();
for (Method method : methods) {
System.out.println(method);
}
methods = cls.getDelcaredMethods();
for (Method method : methods) {
System.out.pringln(method);
}
Method method = cls.getMethod("PersonMethod1", String.class);
Object object = cls.newInstance();
System.out.println(method);
method.invoke(object, "method.invoke");
method = cls.getDelcaredMethod("StudentMethod2", String.class);
method.setAccessible(true);
System.out.println(method);
method.invoke(object, "method.invoke");
}
}
在获取到 Class 对象后,也可以获得其中的属性,获取的方法与获取普通方法类似。描述类中属性的类是 Field 类。
此方法返回 Field 数组,该方法会拿到本类与父类的 public 属性,将其存放至数组中返回,查找顺序是先本类再父类。
该方法也返回的是 Field 数组,但是该方法不会查询父类的属性。
通过此方法查找属性,该方法的参数就是属性的名称,查找范围是先从本类开始查找,接着再查找父类。
通过此方法查找本类中的指定属性,不查找父类。
找到属性的后,可以对它进行设置。
set(Object object, Object....);
第一个参数 obj 是实例化的类对象,第二个参数是要设置的属性值。
get(Object object);
参数是 obj 实例化的类对象,返回值是属性值。
同样,属性也可以利用 setAccessible(Boolean flag) 方法来改变封装性即可。
import java.lang.reflect.Field;
import java.lang.reflect.Method;
class Person {
public int age;
public String name;
Person() {}
public void PersonPrint() {
System.out.println("name=" + name);
}
}
class Student extends Person {
public String grade;
private int classNum;
Student() {}
public void StudentPrint() {
System.out.println("class: " + class);
}
}
public class Main {
public static void main(String[] args) {
Class<?> cls = Student.class;
Field[] fields = cls.getFields();
for (Field field : fields) {
System.out.println(field);
}
fields = cls.getDelcaredFields();
for (Field field : fields) {
System.out.println(field);
}
Field field = cls.getField("name");
Object object = cls.newInstance();
field.set(object, "chenxiliu");
Method method = getMethod("PersonPrint");
method.invoke(object);
field = cls.getDelcaredField("classNum");
field.setAccessible();
field.set(object, 2);
method = getMethod("StudentPrint");
methid.invoke(object);
}
}