一、反射的概述
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象。
以上的总结就是什么是反射
反射就是把java类中的各种成分映射成一个个的Java对象
例如:一个类有:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖,把个个组成部分映射成一个个对象。
二、反射的使用
反射的使用主要有以下几个方面:
1、类加载器
1、获取class对象的三种方式;
2、通过反射获取构造方式、使用构造方法;
3、获取成员变量并调用;
4、获取成员方法并调用;
5、反射main方法;
6、反射方法的其它使用之—通过反射运行配置文件内容;
7、反射方法的其它使用之—通过反射越过泛型检查。
1、获取Class对象的三种方式:
Class类
对象照镜子后可以得到的信息:某个类的数据成员名、方法和构造器、某个类到底实现了哪些接口。对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。一个 Class 对象包含了特定某个类的有关信息。
Class 对象只能由系统建立对象
一个类在 JVM 中只会有一个Class实例
每个类的实例都会记得自己是由哪个 Class 实例所生成
类加载指的是将类的class文件读入内存,并为之创建一个java.lang.Class对象,也就是说当程序使用任何一个类时,系统都会为之生成一个java.lang.Class对象。
类的加载通常由类加载器完成,类加载器通常由JVM提供。
获取Class对象的三种方法如下:
1) 调用对象的getClass()方法,该方法是java.lang.Object中的一个方法;
2) 通过类的class属性来获取该类对应的class对象,如Person.class将返回Person类对应的class对象
3) 使用Class类的forName静态方法:forName(String className)
其中第一种是因为Object类中的getClass方法、因为所有类都继承Object类。从而调用Object类来获取
反射相关的主要API
java.lang.Class:代表一个类
java.lang.reflect.Method:代表类的方法
java.lang.reflect.Field:代表类的成员变量
java.lang.reflect.Constructor:代表类的构造器
三、运行时类
3.1 创建运行时类的对象
调用Class对象的newInstance()方法,该方法会调用类的空参构造器来创建运行时类的对象。该方法正常运行需要2个要求:① 运行时类必须提供空参构造器;② 空参构造器的权限需要够
Class<Person> clazz = Person.class;
Person person = clazz.newInstance();
System.out.println(person);
3.2 获取运行时类的完整结构
3.2.1 获取属性
getFields():获取当前运行时类及其父类中声明为public权限的属性
getDeclaredFields():获取当前运行时类中声明的所有属性(不包含父类)
对所获得的属性调用相应的方法,可获得对应属性的:权限修饰符、数据类型、变量名
3.2.2 获取方法
getMethods():获取当前运行时类及其父类中声明为public权限的方法
getDeclaredMethods():获取当前运行时类中声明的所有方法
对所获得的方法调用相应的方法,可获得对应方法的:注解、权限修饰符、返回值类型、方法名、形参列表、异常
getAnnotations():获取方法上的注解(注解必须是RUNTIME类型才可获取)
3.2.3 获取构造器
getConstructors():获取当前运行时类中声明为public的构造器(不包括父类)
getDeclaredConstructors():获取当前运行类中所有构造器
3.2.4 获取父类及父类的泛型
getSuperclass():获取当前运行时类的父类
getGenericSuperclass():获得当前运行时类带泛型的父类
getActualTypeArguments():获取父类的泛型
3.2.5 获取接口、包、注解
getInterfaces():获取当前运行时类的接口
getPackage():获取当前运行时类所在的包
getAnnotations():获取当前运行时类上的注解
3.3 调用运行时类的指定结构
3.3.1 调用属性
getField(String name):获取对象的某个public属性(不通用)
getDeclaredField(String name):获取对象的某个属性
setAccessible(true):将某个属性设为可访问
get(obj):获取某个对象该属性的值(静态属性可写null)
set(obj,value):设置某个对象该属性的值
3.3.2 调用方法
getMethod(String name,Class… parameterTypes):获取public方法(不通用)
getDeclaredMethod(String name,Class… parameterTypes):获取方法
setAccessible(true):将某个方法设为可访问
invoke(Object obj,Object… args):执行obj对象的该方法,返回所调用方法的返回值(静态方法不用知道具体对象,可写null)
3.3.3 调用构造器
newInstance():调用空参构造器生成对象(运行时类调用,最常用)
getDeclaredConstructor(Class… parameterTypes):获取知道构造器
setAccessible(true):将某个构造器设为可访问
newInstance(Object… args):调用此构造器创建运行时类的对象(运行时类的构造器调用)
Class<Person> aClass = Person.class;
Constructor<Person> constructor = aClass.getConstructor(String.class, int.class);
Person tom = constructor.newInstance("Tom", 12);
System.out.println(tom.toString());
Field age = aClass.getDeclaredField("age");
age.setAccessible(true);
age.set(tom, 10);
System.out.println(tom);
Method show = aClass.getDeclaredMethod("show");
show.invoke(tom);
四、 反射的优缺点:
优点:
能够在运行时动态获取类的实例,提高灵活性
与动态编译结合
缺点:
使用反射性能较低,需要解析字节码,将内存中的对象进行解析
与正常的java代码相比较,效率低。
getMethod和getDeclaredField方法会比invoke和set方法耗时;
随着测试数量级越大,性能差异的比例越趋于稳定;
不要过于频繁地使用反射,大量地使用反射会带来性能问题;
通过反射直接访问实例会比访问方法快很多,所以应该优先采用访问实例的方式。
相对不安全,破坏了封装性(因为通过反射可以获得私有方法和属性)
反射应用场景:
加载配置文件 加载JDBC驱动
通过反射调用Servlet
大部分框架都会使用反射 注入属性 调用方法 实例化等等