Java反射 -- 详细介绍 (框架核心)

news/2024/12/21 18:04:10/

反射 是 Java框架 的核心 ,无论是Tomcat、SpringMVC、Spring IOC、Spring AOP、动态代理 ,都使用到了 反射

反射的作用简单讲就是 无需 new 对象,就可以动态获取到一个类的全部信息,包括 属性、方法,构造器,以及他们的修饰符、参数、注解 等等... 从而构造出 对象实例 并对 对象实例 进行操作

因此,学好反射是必须的,接下来就学习如何使用反射

这是我们下面要进行操作的类

public class Student {private String name;private int age;public String gender;public String address;public Student() {}public Student(String name, int age, String address) {this.name = name;this.age = age;this.address = address;}public Student(String name, int age, String gender, String address) {this.name = name;this.age = age;this.gender = gender;this.address = address;}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 String getGender() {return gender;}public void setGender(String gender) {this.gender = gender;}public String getAddress() {return address;}public void setAddress(String address) {this.address = address;}private void study(){System.out.println("学生在学习");}private void sleep(){System.out.println("学生在睡觉");}public String eat(String something){System.out.println("学生在吃" + something);return "学生已经吃完了,非常happy";}public String toString() {return "Student{name = " + name + ", age = " + age + ", gender = " + gender + ", address = " + address + "}";}
}

获取字节码文件对象(即 类对象)的三种方式

  • 通过 Class类 里面的 静态方法forName("全类名")(最常用)
  • 通过 类名.class 属性获取
  • 通过对象获取字节码文件对象 -- 对象名.getClass()

什么是字节码文件对象?

java文件:就是我们自己编写的java代码。

字节码文件:就是通过java文件编译之后的class文件(是在硬盘上真实存在的,用眼睛能看到的)

字节码文件对象:当class文件加载到内存之后,虚拟机自动创建出来的对象。这个对象里面就包含了类的所有信息。并且该对象在内存中是唯一的(存在于 JVM 的方法区中)

往后 获取类信息 的操作都是建立在 字节码文件对象 的基础上的

这三种获取 字节码文件对象 的方法是有区别的

  • Class.forName("全类名") -- 在 java文件阶段 就可以获取 字节码文件对象,因此这是最常用的
  • 类名.class -- 要在 类加载完成阶段 才能获取
  • 对象名.getClass) -- 要在 对象已经被创建出来的阶段 才能获取

代码示例

//第一种方法
Class clazz1 = Class.forName("com.zhuyuanjie.reflectdemo.Student");//第二种方法
Class clazz2 = Student.class;//第三种方法
Student s = new Student();
Class clazz3 = s.getClass();
System.out.println(clazz1 == clazz2);//true
System.out.println(clazz2 == clazz3);//true

获取 构造方法 并使用

不许通过 new ,我们也能通过反射获取到实例对象

大致步骤 -- 获取构造器对象,调用构造器的 newInstance 方法就可以创建出实例对象

——— 1.首先获取构造器对象 ————

方法如下:

方法名说明
Constructor<?>[] getConstructors()获得所有的构造(只能public修饰)
Constructor<?>[] getDeclaredConstructors()获得所有的构造(包含private修饰)
Constructor getConstructor(Class<?>... parameterTypes)获取指定构造(只能public修饰)
Constructor getDeclaredConstructor(Class<?>... parameterTypes)获取指定构造(包含private修饰)

观察上面表格发现,方法中如果没加 Declareed 就只能获取 public 的构造方法,而加了 Declareed 就可以获取全部,

而且方法末尾如果有 s 是获取所有,没加 s 是获取指定的构造器,需传入 指定构造器的参数列表

代码实例

        //获得class字节码文件对象Class clazz = Class.forName("com.zhuyuanjie.reflectdemo.Student");//获取构造方法对象//获取所有构造方法(public)Constructor[] constructors1 = clazz.getConstructors();for (Constructor constructor : constructors1) {System.out.println(constructor);}//获取指定的空参构造Constructor con1 = clazz.getConstructor();System.out.println(con1);//获取带参数的构造方法Constructor con2 = clazz.getDeclaredConstructor(String.class);System.out.println(con2);

——— 2 调用 newInstance 方法,创建 实例对象———

//1.获取整体的字节码文件对象
Class clazz = Class.forName("com.zhuyuanjie.reflectdemo.Student");
//2.获取空参的构造方法
Constructor con = clazz.getConstructor();
//3.利用空参构造方法创建对象
Student stu = (Student) con.newInstance();
System.out.println(stu);

上面代码中,如果我们获取到的构造方法是 private 属性,那么我们还不能直接调用,需要调用 setAccessible(true)方法 传入参数 true,来临时取消访问权限,如下:

Class clazz = Class.forName("com.zhuyuanjie.reflectdemo1.Student");Constructor con = clazz.getConstructor();//调用 setAccessible(true) 方法
con.setAccessible(true);Student stu = (Student) con.newInstance();
System.out.println(stu);

该方法不止用在构造方法上,包括接下来的 属性、方法 等,只要对 private修饰 的进行操作,就需要调用

获取 成员变量(属性) 并使用

方法名说明
Field[] getFields()返回所有成员变量对象的数组(只能拿public的)
Field[] getDeclaredFields()返回所有成员变量对象的数组,存在就能拿到
Field getField(String name)返回单个成员变量对象(只能拿public的)
Field getDeclaredField(String name)返回单个成员变量对象,存在就能拿到

可以发现以上方法名的结构,与 获取构造方法使用到的方法 一样,这里不在赘述

代码实例

//1.获取class对象Class clazz = Class.forName("com.zhuyuanjie.reflectdemo.Student");//获取成员变量的对象(public + private)Field[] fields2 = clazz.getDeclaredFields();for (Field field : fields2) {System.out.println(field);}System.out.println("===============================");//获得单个成员变量对象//如果获取的属性是不存在的,那么会报异常//Field field3 = clazz.getField("aaa");//System.out.println(field3);//NoSuchFieldExceptionSystem.out.println("===============================");//获取单个成员变量(私有)Field field5 = clazz.getDeclaredField("name");System.out.println(field5);

获取到 Field 对象后,我们就能得到 成员变量 的一切信息

//1.获取class对象Class clazz = Class.forName("com.zhuyuanjie.reflectdemo.Student");Field name = clazz.getDeclaredField("name");//临时取消访问权限name.setAccessible(true);//获取权限修饰符int modifiers = name.getModifiers(); //2//获取属性名String n = name.getName(); // name//获取数据类型Class<?> type = name.getType(); //class java.lang.String//等等... 还有很多,不在展示

还可以获取和修改 成员变量 的值

方法说明
void set(Object obj, Object value)赋值
Object get(Object obj)获取值
        Class clazz = Class.forName("com.zhuyuanjie.reflectdemo.Student");Field field = clazz.getDeclaredField("name");field.setAccessible(true);//设置(修改)name的值//参数一:表示要修改哪个对象的name?//参数二:表示要修改为多少?field.set(s,"张三");//获取name的值//表示我要获取这个对象的name的值String result = (String)field.get(s); // 张三

获取 成员方法 并运行

获取 成员方法 的方法和获取 成员的变量 的方法几乎一样,就是把 Field 改为 Method

而 获取到 成员方法 后,通过调用 Object invoke(Object obj, Object... args) 进行运行

其中

参数一:用obj对象调用该方法

参数二:调用方法的传递的参数(如果没有就不写)

        //1.获取字节码文件对象Class clazz = Class.forName("com.zhuyuanjie.reflectdemo.Student");//2.获取一个对象//需要用这个对象去调用方法Student s = new Student();//3.获取一个指定的方法//参数一:方法名//参数二:参数列表,如果没有可以不写Method eatMethod = clazz.getMethod("eat",String.class);//运行//参数一:表示方法的调用对象//参数二:方法在运行时需要的实际参数//注意点:如果方法有返回值,那么需要接收invoke的结果//如果方法没有返回值,则不需要接收String result = (String) eatMethod.invoke(s, "重庆小面");System.out.println(result);

同样的,如果方法是 private ,也需要调用 setAccessible(true) 方法

除了以上最常用的获取 构造方法 、成员变量 、成员方法 ,在框架中还经常通过反射获取 类 、 属性 、方法 上的注解信息

有需要可以看我的另外一篇博客,地址:

http://t.csdn.cn/qyDp0icon-default.png?t=N6B9http://t.csdn.cn/qyDp0

而且我的 Gitee 上有 Tomcat 、SpringMVC 等框架的核心源码仿写,可以感受下反射在框架中的使用,Gitee 地址如下:

朱元杰的Gitee -- 框架源码仿写https://gitee.com/Speed_Demon/projects


http://www.ppmy.cn/news/973798.html

相关文章

136. 只出现一次的数字

题目 题解一&#xff1a;采用map集合 class Solution {public static int singleNumber(int[] nums) {Map map new HashMap<Integer,Integer>();for (int i 0; i < nums.length; i) {//判断key是否重复&#xff0c;重复直接删掉重复的keyif (map.containsKey(nums[i…

Hive 中 sort by 和 order by 的区别

order by会对输入做全局排序&#xff0c;因此只有1个reducer&#xff08;多个reducer无法保证全局有序&#xff09;&#xff0c;会导致当输入规模较大时&#xff0c;需要较长的计算时间。 sort by不是全局排序&#xff0c;其在数据进入 reducer 前完成排序。 因此&#xff0c;…

第三讲:k8s核心概念和专业术语

序言&#xff1a;这里只对概念继续基础阐述&#xff0c;不做具体案例&#xff0c;这位博主写的特别详细&#xff0c;想要对k8s深入的了解可以跳转了&#xff0c;作为小白的我看的有点懵&#xff0c;毕竟没实践过 链接地址→ http://t.csdn.cn/ZYtEF 这篇文章写了将近两万字对各…

[SQL系列] 从头开始学PostgreSQL 索引 修改 视图

索引 什么是数据库索引 数据库索引是一种单独的、物理的数据库结构&#xff0c;用于对数据库表中一列或多列的值进行排序。它是某个表中一列或若干列值的集合和相应的指向表中物理标识这些值的数据页的逻辑指针清单。 索引提供指向存储在表的指定列中的数据值的指针&#xff0…

Python实战之数据挖掘详解

一、Python数据挖掘 1.1 数据挖掘是什么&#xff1f; 数据挖掘是从大量的、不完全的、有噪声的、模糊的、随机的实际应用数据中&#xff0c;通过算法&#xff0c;找出其中的规律、知识、信息的过程。Python作为一门广泛应用的编程语言&#xff0c;拥有丰富的数据挖掘库&#…

ES6基础知识三:对象新增了哪些扩展?

一、属性的简写 ES6中&#xff0c;当对象键名与对应值名相等的时候&#xff0c;可以进行简写 const baz {foo:foo}// 等同于 const baz {foo}方法也能够进行简写 const o {method() {return "Hello!";} };// 等同于const o {method: function() {return "…

K8S 证书过期后,kubeadm 重新生成证书

前言 K8S 各个组件需要与 api-server 进行通信&#xff0c;通信使用的证书都存放在 /etc/kubernetes/pki 路径下&#xff0c;kubeadm 生成的证书默认有效为 1 年&#xff0c;因此需要定时更新证书&#xff0c;否则证书到期会导致整个集群不可用。 本篇文章主要介绍如何通过 k…

代码随想录-回溯(组合问题)|ACM模式

目录 前言&#xff1a; 77.组合 题目描述&#xff1a; 输入输出示例&#xff1a; 思路和想法&#xff1a; 216. 组合总和 III 题目描述&#xff1a; 输入输出示例&#xff1a; 思路和想法&#xff1a; 17. 电话号码的字母组合 题目描述&#xff1a; 输入输出描述&a…