企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
[TOC] ## **一、反射类的概念及作用** ### **1.什么是反射类** 动态获取类或者对象的信息(属性和方法),以及动态操作对象的属性和方法的类,动态分析和使用类的信息的类 **注**:动态是指在java运行状态 ### **2.反射类的作用** &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * 动态获取:动态获取类或对象的属性和方法 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * 动态调用:动态调用对象的属性和方法 ### **3.反射的使用场景** &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;* 操作因访问权限限制的属性和方法; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;* 实现自定义注解; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;* 动态加载第三方jar包 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;* 按需加载类,节省编译和初始化时间; <br> <br> ## **二、反射技术的使用** ### **1.反射主要涉及的类** :-: ![](https://img.kancloud.cn/70/73/7073d541b5ea5f050331f04eb3ad0486_1127x584.png) 反射技术的核心技术是Class对象,每个类在定义以后都有各自的Class对象 <br> | 类名 | 用途 | | --- | --- | | Class类 | 代表类的实体,在运行的Java应用程序中表示类和接口 | | Field类 | 代表类的成员变量(成员变量也称为类的属性) | | Method类 | 代表类的方法 | | Constructor类 | 代表类的构造方法 | <br> <br> ### **2.Class类** [Class](https://developer.android.google.cn/reference/java/lang/Class)代表类的实体,在运行的Java应用程序中表示类和接口。在这个类中提供了很多有用的方法,这里对他们简单的分类介绍。 * **获得类相关的方法** | 方法 | 用途 | | --- | --- | | asSubclass(Class clazz) | 把传递的类的对象转换成代表其子类的对象 | | Cast | 把对象转换成代表类或是接口的对象 | | getClassLoader() | 获得类的加载器 | | getClasses() | 返回一个数组,数组中包含该类中所有公共类和接口类的对象 | | getDeclaredClasses() | 返回一个数组,数组中包含该类中所有类和接口类的对象 | | forName(String className) | 根据类名返回类的对象 | | getName() | 获得类的完整路径名字 | | newInstance() | 创建类的实例 | | getPackage() | 获得类的包 | | getSimpleName() | 获得类的名字 | | getSuperclass() | 获得当前类继承的父类的名字 | | getInterfaces() | 获得当前类实现的类或是接口 | * **获得类中属性相关的方法** | 方法 | 用途 | | --- | --- | | getField(String name) | 获得某个公有的属性对象 | | getFields() | 获得所有公有的属性对象 | | getDeclaredField(String name) | 获得某个属性对象 | | getDeclaredFields() | 获得所有属性对象 | * **获得类中注解相关的方法** | 方法 | 用途 | | --- | --- | | getAnnotation(Class annotationClass) | 返回该类中与参数类型匹配的公有注解对象 | | getAnnotations() | 返回该类所有的公有注解对象 | | getDeclaredAnnotation(Class annotationClass) | 返回该类中与参数类型匹配的所有注解对象 | | getDeclaredAnnotations() | 返回该类所有的注解对象 | * **获得类中构造器相关的方法** | 方法 | 用途 | | --- | --- | | getConstructor(Class... parameterTypes) | 获得该类中与参数类型匹配的公有构造方法 | | getConstructors() | 获得该类的所有公有构造方法 | | getDeclaredConstructor(Class... parameterTypes) | 获得该类中与参数类型匹配的构造方法 | | getDeclaredConstructors() | 获得该类所有构造方法 | * **获得类中方法相关的方法** | 方法 | 用途 | | --- | --- | | getMethod(String name, Class... parameterTypes) | 获得该类某个公有的方法 | | getMethods() | 获得该类所有公有的方法 | | getDeclaredMethod(String name, Class... parameterTypes) | 获得该类某个方法 | | getDeclaredMethods() | 获得该类所有方法 | * **类中其他重要的方法** | 方法 | 用途 | | --- | --- | | isAnnotation() | 如果是注解类型则返回true | | isAnnotationPresent(Class annotationClass) | 如果是指定类型注解类型则返回true | | isAnonymousClass() | 如果是匿名类则返回true | | isArray() | 如果是一个数组类则返回true | | isEnum() | 如果是枚举类则返回true | | isInstance(Object obj) | 如果obj是该类的实例则返回true | | isInterface() | 如果是接口类则返回true | | isLocalClass() | 如果是局部类则返回true | | isMemberClass() | 如果是内部类则返回true | ### **3.Field类** [Field](https://developer.android.google.cn/reference/java/lang/reflect/Field)代表类的成员变量(成员变量也称为类的属性)。 | 方法 | 用途 | | --- | --- | | equals(Object obj) | 属性与obj相等则返回true | | get(Object obj) | 获得obj中对应的属性值 | | set(Object obj, Object value) | 设置obj中对应属性值 | ### **4.Method类** [Method](https://developer.android.google.cn/reference/java/lang/reflect/Method)代表类的方法。 | 方法 | 用途 | | --- | --- | | invoke(Object obj, Object... args) | 传递object对象及参数调用该对象对应的方法 | ### **5.Constructor类** [Constructor](https://developer.android.google.cn/reference/java/lang/reflect/Constructor)代表类的构造方法。 | 方法 | 用途 | | --- | --- | | newInstance(Object... initargs) | 根据传递的参数创建类的对象 | <br> ### **6.反射技术主要框架** :-: ![](https://img.kancloud.cn/11/68/116874fed16316023ce285d63d19d02a_1350x732.png) <br> <br> ## **三、反射操作示例** ### 3.1:反射小案例 ~~~ 首先我创建个Student.java的类,里面有三个属性(name,age,credit),看代码: ~~~ ``` package day06; public class Stundet { // private在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部类) private String name; private int age; private boolean credit; public String getName(){ return name; } public void SetName(String name){ this.name = name; } public int getAge(){ return age; } public void SetAge(int age){ this.age = age; } public boolean getcredit(){ return credit; } public void SetCreadit(boolean credit){ this.credit = credit; } public Stundet(String name,int age,boolean credit){ super();//调用父类中的某一个构造函数(应该为构造函数中的第一条语句)。 this.name = name; // this 是一个指向本对象的指针 this.age = age; this.credit = credit; } public Stundet(){ } } ``` <br> 接下来我们要创建一个测试的类,我给他取名字叫做test1,你们可以根据自己来创建这个类哦。 我们需要认识三中获取类的方式: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;* 类名.class &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;* Class.ForName 注:这个是一个方法,我们需要往里面传递参数,具体传入什么参数我接下来会说的呢, &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;* 对象.getclass 当我们创建好了测试的类后,我们这里讲解的是反射,我们当然要通过反射的方式来加载到JVM,这里就需要我上面说的Class.forName的方式来加载,这个方法是返回的一个Class对象。 <br> ``` package day06; public class test1 { public static void main(String args[]) throws IllegalAccessException, InstantiationException, ClassNotFoundException { Class studentClass = Class.forName("day06.Stundet"); //加载类,通过Class类型接受 System.out.println(studentClass); Stundet student = (Stundet)studentClass.newInstance(); //实际是使用的无参构造实例化对象 student.SetAge(16); //使用类中的方法 System.out.println(student.getAge()); //输出值,验证是否成功复制 } } ``` **运行结果:** class day06.Stundet 16 <br> **文件结构图** ![](https://img.kancloud.cn/cb/e3/cbe343525ddcfbf26b2211d4cee73ef2_188x64.png) <br> <br> ### 3.2:分别通过该类的有参构造和无参构造来实例化对象   类已经加载到了内存中了,我们是不是应该创建个Student的对象呢,我们都知道在平时普通的new一个对象的时候,其实默认是调用了该类的构造方法,那么这里也是如此我们也需要拿到该类的构造方法,这小结我们要需要如何获取类的构造方法: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;* 新增需要知识:获取构造方法 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;* 使用的方法:getConstructor() 和 newInstance() 我们看下如何通过无参的构造来实例化对象,这里我们只需要使用Class的对象点出newInstance()即可,这样系统就会实例化一个对象。 问:我调用了newInstance(),是如何实例化对象的呢?上面说要通过构造函数,我也没有指定构造函数呀。 答:使用次方法系统会自动寻找类中的无参的构造方法,通过无参的构造来实例化对象。这里大家可以测试下,当你删除类中的无参构造时,你再次运行代码会出现错误。 ``` public static String stu() throws IllegalAccessException, InstantiationException, ClassNotFoundException { Class StuClass = Class.forName("day06.Stundet");// 加载类 Stundet sutden = (Stundet)StuClass.newInstance(); // 实际是使用的无参构造函数实例化对象 sutden.SetAge(19); // 使用类中方法 System.out.println(sutden.getAge()); // 输出值,验证是否成功赋值 System.out.println("执行完成"); return null; } ``` **运行结果:** 19 执行完成 <br> ### 3.2:使用有参数的构造来实例化对象 首先我们要先拿到带有参数的构造方法,在我上面写的方法列表内有相关的方法,我们这里使用的是**getConstructor()** 来获取带参的构造,先来了解下代码: ``` Constructor constructor =studentClass.getConstructor(String.class,int.class,boolean.class); Student student = (Student) constructor.newInstance("Candy",20,true); System.out.println(student.getAge()); ``` 这里大家看下的,在getConstructor()如果括号额你填的空则是使用的是空参数构造方法,填写参数的意思是因为我们每个类中可能会存在大量重名的方法,比如我们使用的方法重载,这里可以通过参数列表来区分使用的是哪一个方法。当你传入了参数的时候,在你使用newInstance时候你会发现代码有提示说让您传入参数,这里的参数就是你平时实例化对象时的实际参数。 <br> <br> ### **3.4:如何通过反射来使用类中的方法**   我们这里可以通过参考上面通过有参数的构造来实例化对象的代码来处理使用类中的方法,其实这里的思路是一样的。遵循三个步骤(这里对于我来说的是那个步骤)1.拿到类中的方法(Look,反射屌不屌)2.给方法传递参数,3.测试是否成功。这里的获取方法的方法是需要传入两个参数 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;参数一:你需要的方法的名称 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;参数二:方法参数的类型,有多少写多少,反正代码里面接受的是三个点可编长参数。 最后使用invoke方法来进行使用方法,两个参数: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;参数一:上面咱们实例化的student。 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;参数二:你需要往这个方法里面传的值 ``` Method method = clzzClass.getDeclaredMethod("setAge", int.class); method.invoke(user, 20); System.out.println(user.getAge()); ``` <br> <br> ## **四、反射的优点及缺点** <br> ### **1.反射的优点** * 灵活、自由度高:不受类的访问权限限制,想对类做啥就做啥 <br> <br> ### **2.反射的缺点** * (1)使用反射的性能较低 * (2)使用反射相对来说不安全 * (3)破坏了类的封装性,可以通过反射获取这个类的私有方法和属性