14、反射

130 阅读3分钟

一、概念

反射:在程序运行期间,可以动态的创建类的对象、获取类中的成员,对类中的成员进行调用

如果要知道一个类的内部结构,就必须先获取这个类的二进制字节码文件对象

二进制字节码文件对象也称为类对象

类的对象:基于某个类通过new关键字创建出来的一个实例

类对象:类在加载后的产物,它里面封装一个类所包含的所有信息,有:类名、成员变量、成员方法、构造函数、继承的父类、实现的接口

注:任何一个类在加载到内存后所得到的类对象是唯一的

二、获取类对象

// 1.通过对象的getClass()方法
Student s = new Student();
Class c1 = s.getClass();
​
// 2.通过类的静态属性class
Class c2 = Student.class;
​
// 3.通过Class的静态方法forName(String s),参数:全限定类名
Class c3 = Class.forName("com.qf.test.Student");

三、类对象的常用方法

1.getName(): 获取全类名,形如: com.qf.test1.Student
2.getSimpleName(): 获取类名,形如: Student
3.getSuperclass(): 获取父类的全类名
4.getInterfaces(): 获取该类实现的所有接口,返回的是Class数组
5.getPackage(): 获取该类的包,返回的是Package对象
6.newInstance(): 创建指定类的对象,注:这个方法只能通过类中的无参构造函数创建对象,如果没有这个构造函数会发生InstantitionException、NoSuchMethodException

四、获取构造函数,创建对象

1.getConstructors(): 获取类中所有public的构造函数
2.getDeclaredConstructors(): 获取类中的所有构造函数
3.getConstructor(Class...c): 获取public构造函数中指定参数的构造函数
4.getDeclaredConstructor(Class...c): 获取指定参数的构造函数
// 如果指定构造函数不存在,会发生NoSuchMethodException
private Work(Integer i){
 System.out.println("私有构造函数:"+ i);
}
// 注:参数是基本类型和包装类是不同的,例如:在获取这个构造函数对象时,参数必须是Integer.class,不能是int.class

通过构造函数对象创建实例:newInstance(Object...o)

注:

  1. 创建对象时的实参与获取到的构造函数对象的形参不同,会发生IllegalArgumentException非法参数异常,原因:1. wrong number of arguments 2.argument type mismatch
  2. 通过私有构造函数创建对象,需要在创建对象之前调用setAccessible(true)表示允许访问私有内容,否则会发生IllegalAccessException。这种做法不推荐,因为它破坏了封装性。

五、获取成员变量,并赋值

1.getFields(): 获取类中所有public的成员变量
2.getDeclaredFields(): 获取类中的所有成员变量
3.getField(String name): 获取指定的public的成员变量
4.getDeclaredField(String name): 获取指定的成员变量
// 如果指定的成员变量不存在会发生NoSuchFieldException

通过成员变量对象对变量进行赋值和获取:

  1. 赋值:set(Object o,Object value)
  2. 获取:get(Object o)

注:

  1. 赋值的类型与变量的类型不一致会发生IllegalArgumentException
  2. 为私有成员变量赋值或者获取私有变量的值,需要在赋值、获取之前调用setAccessible(true)表示允许访问私有内容,否则会发生IllegalAccessException。这种做法不推荐,因为它破坏了封装性。

六、获取成员方法,并调用

1.getMethods(): 获取本类和父类中的所有public的成员方法
2.getDeclaredMethods(): 获取本类中的所有成员方法
3.getMethod(String name,Class...c): 获取类中指定的Public方法
4.getDeclaredMethod(String name,Class...c): 获取类中的指定方法

调用方法:invoke(Object o,Object...args)

注:

  1. 调用方法时,实参的个数类型必须与形参一致,否则会发生IllegalArgumentException非法参数异常,原因:1. wrong number of arguments 2.argument type mismatch
  2. 调用私有方法,需要在调用之前,使用setAccessible(true)表示允许访问私有内容,否则会发生IllegalAccessException。这种做法不推荐,因为它破坏了封装性。

七、案例

// 泛型擦除
Class c = ArrayList.class;
Method m = c.getMethod("add",Object.class);
ArrayList<String> list = new ArrayList<>();
m.invoke(list,1);
m.invoke(list,"abc");
m.invoke(list,3.3);
m.invoke(list,false);
m.invoke(list,new Stu());
System.out.println(list);