链接:https://zhuanlan.zhihu.com/p/25057658
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
反射(Reflection)被视为 动态语言的关键。
反射机制 是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态的获取信息以及动态调用对象的方法的功能称为Java语言的反射机制。
加载完类后,在堆内存中会产生一个 Class 类型的对象(一个类只有一个Class对象),这个对象包含了完整的类的结构信息,而且这个Class 对象就像一面镜子,透过这个镜子可以看到类的完整结构,所以称之为“反射”。
反射相关的主要API :
》java.lang.Class : 代表一个类
》java.lang.reflect.Method : 代表类的方法
》java.lang.reflect.Field : 代表类的成员变量
》java.lang.reflect.Constructor : 代表类的构造方法
未学反射之前,如何创建对象,并调用其中的方法、属性。
Class clazz = Person.class 对应一个Class(这是一个类)的实例,相当于Person 这个运行时类本身的.class 文件对应的类充当了 Class 的实例。clazz :栈空间引用,Person.class 堆空间实体。Person 类本身的所有的东西 对 clazz 完全公开。
一:理解反射的源头 Class 类
Class 类:是反射的源头,Java 程序编译完成后,生成的每一个.class 文件(在内存中,叫运行时类)就是一个Class 的一个实例。
Java 编译器编译好java 文件后,产生.class 文件在磁盘中,这种class 文件是二进制文件,内容是只有JVM虚拟机能是别的机器码,JVM虚拟机读取字节码文件,取出二进制数据,加载到内存中,解析.class 文件内的信息,生成对应的 Class 对象。
注意:
1.每一个运行时类只加载一次。
2.可以获取对应的运行时类的完整结构(属性、方法、构造器、内部类、父类、所在的包、异常、注解...)
3.调用对应的运行时类的指定的结构(属性、方法、构造器)
4.反射的应用:动态代理。
二:如何获取Class 的实例(4种)
1.调用运行时类本身的.class
2.通过运行时类的对象获取
3.通过Class 的静态方法获取。Class.forName(className),其中的className 也就是要获取的类的类路径(注意此时会有异常,因为类有可能不在)
4.通过类的加载器
三、有了Class 实例后,便可以创建运行时类的对象:
调用Class 的newInstance()方法,创建运行时类的对象。
要求:1)类必须要有一个无参的构造器。
2)类的构造器的访问权限足够(能被使用)。
四、通过反射调用类得完整结构
(一)获取属性
1)getField(String):获取运行时类中声明为public 的指定属性名为***的属性。
2)getDeclaredField(String) : 获取运行时类中指定的名为...的属性
属性名.setAccessible(true) :由于属性权限符的限制,为了保证可以给属性赋值,需要在操作前使此属性可被操作。
若要调用非静态属性,需要有对应的对象。
3)getFields()方法,获取 运行时类及其父类中的 对应的 所有的public属性,返回值类型Field[] 数组形式。
4)getDeclaredFields()方法,能获取到 运行时类本身的所有属性,无关访问权限。
5) 获取属性的各个部分的内容。
①获取每个属性的权限修饰符
格式:获取到的属性.getModifiers();返回值类型为int {1:public 2:private 0:缺省,4: protected, 若想不显示数字,而显示public等,就要翻译过来,调用 Modifier.toString(int),返回值类型为String}
②获取属性的变量类型
格式:获取到的属性.getType();返回值类型 Class
③获取属性名
格式:获取到的属性.getName();返回值类型 String
(二)获取方法
1)非静态方法
①公有的(public)
1.需要指定方法名,方法参数类型(String.class)
Method m = clazz,getMethod("show");方法名 为 show,形参没有。
2.调用该指定的方法:m.invoke(对象,参数列表),形参若没有,就不用写
②私有的(缺省,protected,private)
1.getDeclaredMethod(.....);
2.Method对象.setAccessible(true);设置对方法允许访问
3.调用方法:Method对象.invoke(.......);
2)静态方法,不需要创建对象
①需要指定方法名
②调用该指定的方法:m.invoke(对象,参数列表),如:m.invoke(Person.class)
(三)调用构造器
1.Class 类实例.getDeclaredConstructor(参数实例);返回值类型Constructor。
Class类的实例.getDeclaredConstructors(); 返回值Constructor[] 数组。
2.必要时加上 Constructor 对象.setaccessible(true);
3.Constructor 对象.newInstance(...);来创建运行时类对象。