我们先用一个思维导图来说一下关于Java反射的知识点
![](https://img-blog.csdnimg.cn/img_convert/c0db7a2685deb6315397140578e37a45.png)
一 Java反射
1.1 什么是Java反射
在java的面向对象编程过程中,通常我们需要先知道一个Class类,然后new 类名()方式来获取该类的对象。也就是说我们需要在写代码的时候 (编译期或者类加载之前)就知道我们要实例化哪一个类,运行哪一个方法,这种通常被称为 静态的类加载
那么有没有一种方法 在运行期动态的改变程序的调用行为的方法 呢?
这就是要为大家介绍的“java反射机制“。
那么java的反射机制能够做那些事呢? 大概是这样几种:
在程序运行期动态的根据package名.类名实例化类对象。
在程序运行期动态获取类对象的信息,包括对象的成本变量和方法。
在程序运行期动态使用对象的成员变量属性。
在程序运行期动态调用对象的方法 (私有方法也可以调用)。
总的来说,反射API用来生成类,接口或者对象的信息。
Class类:反射的核心类,可以获取类的属性,方法等信息。
Field类:Java.lang.reflec包中的类,表示类的成员变量,可以用来获取和设置类之中的属性值。Method类:Java.lang.reflec包中的类,表示类的方法,它可以用来获取类中的方法信息或者执行方法。
Constructor类:Java.lang.reflec包中的类,表示类的构造方法。
1.2 反射使用步骤(获取Class对象、调用对象方法):
获取想要操作的类的Class对象,他是反射的核心,通过Class对象我们可以任意调用类的方法。
调用Class类中的方法,既就是反射的使用阶段。
使用反射API来操作这些信息。
1.2 反射能做什么?
我们知道反射机制允许程序在运行时取得任何一个已知名称的class的内部信息,包括包括其modifiers(修饰符),fields(属性),methods(方法)等,并可于运行时改变fields内容或调用methods。那么我们便可以更灵活的编写代码,代码可以在运行时装配,无需在组件之间进行源代码链接,降低代码的耦合度;还有动态代理的实现等等;但是需要注意的是反射使用不当会造成很高的资源消耗!
二 类加载与反射关系
java执行编译的时候将java文件编译成字节码class文件,类加载器在类加载阶段将class文件加载到内存,并实例化一个java.lang.Class的对象。比如对于Student类在加载阶段会有如下动作:
在内存(方法区或叫代码区)中实例化一个Class对象,注意是Class对象不是Student对象。
一个Class类(字节码文件)对应一个Class对象,并且只有一个
该Class对象保存了Student类的基础信息,比如这个Student类有几个字段 (Filed) ? 有几个构造方法(Constructor)? 有几个方法 (Method) ? 有哪些注解 (Annotation)? 等信息
有了上面的关于Student类的基本信息对象 (java.lang.Class对象),在运行期就可以根据这些信息来实例化Student类的对象。
在运行期你可以直接new一个Student对象
也可以使用反射的方法构造一个Student对象
三 操作反射的Java类
了解了上面的这些基础信息,我们就可以更深入学习反射类相关的类和方法了:
java.lang.Class:代表一个类
java.lang.reflect.Constructor: 代表类的构造方法
java.lang.reflect.Method: 代表类的普通方法
java.lang.reflect.Field: 代表类的成员变量
java.lang.reflect.Modifier: 修饰符,方法的修饰符,成员变量的修饰符。
java.lang.annotation.Annotation: 在类、成员变量、构造方法、普通方法上都可以加注解
3.1 什么是Class类
对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。一个 Class 对象包含了特定某个结构(class/interface/enum/annotation/primitive type/void/[])的有关信息。
Class本身也是一个类
Class 对象只能由系统建立对象
一个加载的类在 JVM 中只会有一个Class实例
一个Class对象对应的是一个加载到JVM中的一个.class文件
每个类的实例都会记得自己是由哪个 Class 实例所生成
通过Class可以完整地得到一个类中的所有被加载的结构
Class类是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象
3.2 获取Class对象的三种方法
Class.forName()方法获取Class对象
![](https://img-blog.csdnimg.cn/img_convert/84d5424d1a99c9e3a89e520e886761e3.png)
类名.class获取Class对象
![](https://img-blog.csdnimg.cn/img_convert/5ab892d390d70003c2cb2528f4813808.png)
类对象.getClass()方式获取Class对象
![](https://img-blog.csdnimg.cn/img_convert/338fd404e04708038268c3fa62ef66bf.png)
3.2 获取Class对象的成员变量
//通过全类名加载类的class对象
Class cla = Class.forName("com.zy.service.impl.BookServiceImpl");
//class对象可以认为是类的手术刀,可以解剖类里面的东西--属性,构造器,方法
Field[] fields = cla.getFields();//只能获取public修饰的属性
for (Field field : fields) {
System.out.println(field);
}
System.out.println("------------");
Field[] fields1 = cla.getDeclaredFields();//获取所有属性
for (Field field : fields1) {
System.out.println(field);
getFields(方法获取类的非私有的成员变量,数组,包含从父类继承的成员变量
getDeclaredFields方法获取所有的成员变量,数组,但是不包含从父类继承而来的成员变量
3.3 获得Class对象的方法
getMethods0:获取Class对象代表的类的所有的非私有方法,数组,包含从父类继承而来的方法
getDeclaredMethods0:获取Class对象代表的类定义的所有的方法,数组,但是不包含从父类继承
而来的方法
getMethod(methodName): 获取Class对象代表的类的指定方法名的非私有方法
getDeclaredMethod(methodName): 获取Class对象代表的类的指定方法名的方法
获取参数相关的属性:
获取方法参数个数: getParameterCount()
获取方法参数数组对象: getParameters(),返回值是java.lang.reflect.Parameter数组获取返回值相关的属性
获取方法返回值的数据类型: getReturnType()
需要注意的是:
一个类在 JVM 中只会有一个 Class 实例 ,即我们对上面获取的 c1,c2,c3进行 equals 比较,发现都是true
通过 Class 类获取成员变量、成员方法、接口、超类、构造方法等
3.4 创建类的对象(实例化对象)
//1.通过全类名获取类的class对象
Class cla = Class.forName(clasName);
//2.通过Class对象获取类的无参构造器
Constructor constructor = cla.getDeclaredConstructor();
//3.使用构造器创建对象
BookService bookService = (BookService) constructor.newInstance();
四 反射的优缺点
优点:自由,使用灵活,不受类的访问权限限制。可以根据指定类名、方法名来实现方法调用,非常适合实现业务的灵活配置。在框架开发方面也有非常广泛的应用,特别是结合注解的使用。
缺点:也正因为反射不受类的访问权限限制,其安全性低,很大部分的java安全问题都是反射导致的。相对于正常的对象的访问调用,反射因为存在类和方法的实例化过程,性能也相对较低破坏java类封装性,类的信息隐藏性和边界被破坏。