基础篇-反射Relaction

news/2025/2/7 6:17:16/

什么是反射?

在堆中:每个类都拥有对应唯一属于自己的Class对象,得到Class类型对象便得到了这个类元数据
举例子:每个人都拥有对应唯一数据自己的灵魂,得到灵魂便等于得到了这个人的全部细节

反射能做什么?

由上面的逻辑我们进行反推,是否可以这样?

1.通过灵魂来获取这个人的全部信息
2.通过Class类型对象获取这个类的完整结构

通常你是如何调用方法的?反射又是如何调用方法

常规:创建对象➡对象调用方法
反射:获取Class对象➡获取实例对象和获取Method对象➡通过Method对象执行invoke函数执行方法

说到这里你可能不明白我再说什么,于是上代码瞧瞧

//1.加载类,获取Class类型对象【重点】
Class aclass = Class.forName(class_full_path);
//2.通过Class类型对象获得"你刚才使用的未知类限定名"的对象实例
Object o = cls.newInstance();
//由于:通过o.getClass可以清楚的查看到运行类型实际是Cat,所以可以直接强转
Cat cat = (Cat)cls.newInstance();
//通过cls获取"你刚才使用的未知类限定名里的未知方法的"Method类型对象
Method method = cls.getMethod(method_name);
//亮点:万物皆对象,方法也是对象
//通过Method类型对象通过Instance_Object对象进行调用方法
method.invoke(o);
//亮点:常规调用方法Object_Instance.method_name,反射Method.invoke(Object_Instance);

说人话,所以我要提到类的加载阶段

运行阶段:另一份源码文件new调用指定了的字节码文件,于是对应的字节码文件触发被加载
编译阶段:源码文件(人能看懂的代码)➡ Javac编译产生 ➡字节码文件 (计算机能看懂的代码)
加载阶段:将你指定new的类的字节码二进制数据加载到堆中,产生Class类型对象(灵魂)并被对象所映射

反射依赖:类加载器ClassLoder加载时产生 Class对象中的结构(Fieid[]对象 Constructor[]对象 Method[]对象)
我们现在来通过类的结构来进行操作对象,而不是通过对象操作类的结构

废话不多说来说说反射的实际用处

1.运行时判断任意一个对象的所属的类
2.运行时构造任意一个类的对象
3.运行时得到所具有的成员变量 方法 构造方法
4.运行时调用任意一个成员变量 方法 构造方法
5.生成动态代理Proxy

反射这么牛皮没有缺点吗?

优点:动态灵活
缺点:进行解释执行,进行Accessible访问安全检查,影响速度,可以手动将其关闭,略微提升

总体来讲,利大于弊,用它就完事

API重要的对象有什么Java.lang.reflection

  • Class 【核心】
  • Field
  • Constructor
  • Method

反射的一切围绕Class对象操作,这个核心对象我该如何获取?

首先:不同的加载阶段获取方法不同

编译阶段加载阶段运行阶段类的加载器
Class.forName(class_all_path);类名.class();对象.getClass();先获取实例对应类加载器,通过类加载器获得Class对象

使用场景也不同

编译阶段加载阶段运行阶段类的加载器
读取配置文件➡读取类全路径➡加载类用于属性参数的传递有对象实例获取关联的加载时堆区对应的Class类型对象万能场景

而特殊的基本类型通过反射获取的是对应包装类

其他类型能否获取Class对象,万物皆对象,完全ojbk

  1. 内部类(成员 静态 局部 匿名)
  2. 外部类
  3. 接口
  4. 数组/二维数组
  5. 注解
  6. 枚举
  7. 基本数据类型
  8. void/Class

关于Class对象的小细节

  1. Class类的父类:所有类的基类是Object,他的老爸也是-obj欧比节
  2. 加载阶段的Class类型对象是系统进行自动加载的,不是你手动写的程序加载的
    理据1:进行New创建对象,使用Debug强行步入即可进入ClassLoader.loadClass(class_full_path);
    理据2:进行反射获取Class对象,使用Debug强行步入即可进入ClassLoader.loadClass(class_full_path);
  3. 同一个类型对象在运行阶段操作调用进入加载阶段仅加载一次Class类型对象,内存中仅有一份
    理据:不同方式获取同一个类的Class类型对象,进行hashCode对比,结果为true
    反推:不同类的Class类型对象,进行hashCode对比,结果为false
  4. 每个实例对象都会映射到唯一属于自己的Class类型对象 ,你只有唯一的灵魂
    理据1:通过对象实例getClass即可获得对应的Class类型对象
    理据2:同类多个实例进行getClass获取的Class对象hashCode对比为true
  5. 通过Class类型对象可以获得这个类的完整结构(元数据),一系列API
    API:获取类名,获取实例,各种元数据名称,继承的父类对象,继承的父类对象的Class类型对象,实现的接口,类的加载器,构造,方法,成员,各种权限
  6. Class对象存放在堆中,底层引用二进制数据,对二进制数据进行封装为数据结构方便你的使用
  7. 类的字节码二进制数据,放在方法区
    理据:方法区的类二进制数据(元数据)与堆中Class类型对象产生映射,Class类型对象将元数据封装为数据结构方便操作

类从编写到运行的宏观过程

编写编译加载运行
程序员编写代码JVM进行编译JVM进行加载JVM进行运行
产生java.文件产生.Class文件堆中产生Class对象&&方法去产生二进制元数据堆中产生对象并映射到堆中Class对象

类的加载微观过程

微观类的加载过程
类的二进制元数据加载到方法区 堆中生成Class对象并记录二进制元数据的位置

静态加载与动态加载

💙静态加载:编译阶段➡类加载 >>先解析检查引用是否存在,引用的类文件必须编写完成提前加载,依赖性强
🧡动态记载:运行阶段➡类加载 >>运行时才解析检查引用是否存在,此时引用类文件不存则不通过,降低依赖

如何触发类的加载

1.通过new创建对象(💙静态加载)
2.子类被加载时,触发父类的加载(💙静态加载)
3.调用类的静态成员,触发当前依赖类的加载(💙静态加载)
4.通过反射,动态加载时(🧡动态加载)

关于类加载阶段-加载

一个类仅加载一次,不会重读加载类的数据
理据:➡触发类的加载时进行Debug步入➡到loadClass()方法

关于类加载阶段-链接-效验

理据:➡触发类的加载时进行Debug步入➡到loadClass()方法➡步入SecurityMange对象
配置:➡Xverify:none 关闭验证措施

关于类加载阶段-链接-准备

准备阶段防止多线程时分配多个内存,于是对多线程进行了加锁
理据:➡触发类的加载时进行Debug步入➡到Client()方法(Synchronaized自动锁)

类加载器双亲委派模型

在这里插入图片描述

获取类加载器的方法

//扩展类加载器Main
ClassLoader classLoader = MainTest.class.getClassLoader();
//表示当前线程的类加载器——应用程序类加载器
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
//—启动类加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();

反射过程中将会遇见的问题?

  1. 你会发现了修饰父为private的可以获取,但无法进行使用,使用setAccessible关闭访问权限,就可以进行执行了
  2. 你会发现一个类若是没有无参构造器,你无法直接newInstance创建实例,其实反射主要是通过构造器来创建对象的,直接newInstance等价于无参构造创建对象,若没有无参构造,就需要先通过反射获取有参构造器才能进行实例化
  3. 由于构造器是重载有多个的,所以需要通过参数类型的Class对象获取指定的构造器
  4. 对于静态的成员,setting和getting无需指定对象实例,使用null可以,当然指定对象也可以,毕竟指定了一处,由于静态在client函数执行时仅收集一次,所以一处修改,处处修改。
注意:对于静态属性,则set和get中的指定对象obj,由于静态仅加载一次,所以可以更改一处等于所有的对象静态初始化参数内容,可以写成null无需指定对象
1.获取这个类的Class类型对象
Class clazz = Class.forName(class_full_path);
2.进行实例化创建对象
Object obj = clazz.newInstance();
3.通过Class类型对象获取指定的public Field类型对象
Field field = clazz.getField(Field_name_String);
3.通过Class类型对象获取指定的所有访问权限 Field类型对象
Field field = clazz.getDeclaredField(Field_name_String);
field.setAccseeible(ture);
4.通过实例化对象和参数内容进行setting
field.set(obj,Field_content);
field.set(null,Field_content);
5.获取属性的值
field.get(obj);
field.get(null);
  1. 对于静态的方法,invoke无需指定对象实例,使用null可以,当然指定对象也可以,若有返回值,返回的是Object类型,raVal.getClass().hasCode() = String.hasCode();
注意:对于静态方法,调用方法invoke时的指定对象obj,由于静态仅加载一次,所以可以更改一处等于调用所有对象唯一的静态方法,可以写成null无需指定对象
1.获取这个类的Class类型对象
Clazz<?> clazz = Class.forName(class_full_path);
2.创建对象获取实例
Object obt = clazz.newInstance();
3.获取指定public修饰的Method类型对象(防止重载所以进行参数类型指定,方法名,方法参数class...类型)
Method method = clazz.getMethod(method_name_string,Class...clazz);
3.获取指定全部访问权限的Method类型对象(防止重载所以进行参数类型指定,方法名,方法参数class...类型)
Method method = clazz.getDeclaredMethod(method_name_string,Class...clazz);
method.setAccessible(true);
4.执行方法
method.invoke(obj,Method_Field_content...content);
method.invoke(null,Method_Field_content...content);
4.若有返回值,同一返回类型Object类型
Object raVal = method.invoke(obj,Method_Field_content...content);
注意:(运行类型和方法定义返回值一致),

核心API

类结构

  • getName()获取全类名
  • getSimpleName()获取简单类名
  • getPackage()返回本类Package包信息
  • getSuperClass()返回Class形式的父类信息
  • getInterfaces()返回Class形式全部接口
  • getAnnotations()返回全部注解信息
  • getFields()获取所有public修饰的属性,包括本类及父类
  • getDeclaredField()获取本类中所有属性
  • getMethods()获取所有public修饰的方法,包括本类以及父类
  • getDeclaredMethods()获取本类所有方法
  • getConstructors()获取所有public修饰的构造器
  • getDeclaredConstructor是()获取本类所有构造器

构造器结构

  • getModifier()以int形式返回修饰符 默认0 public1 private2 protected4 static8 final6
  • getName()获取构造名
  • getParameter()Types返回所有参数类型

属性结构

  • getModifiers()以int形式返回修饰符 默认0 public1 private2 protected4 static8 final6
  • getType()以Class形式返回属性类型
  • getName()返回属性名

方法结构

  • getModifier()以int形式返回修饰符 默认0 public1 private2 protected4 static8 final6
  • getReturnTpye()返回返回值类型Class形式
  • getName()返回方法名
  • getparameterTypes()返回所有参数类型

不要在该奋斗的年纪选择安逸。


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

相关文章

stol函数在linux下使用,技术|在 Linux 命令行下使用“原力”

和绝地武士的原力一样&#xff0c;-f 参数是很强大的&#xff0c;并伴随着潜在的毁灭性&#xff0c;在你能用好的时候又很便利。 近些年来&#xff0c;科幻发烧友开始在每年的 5 月 4 日庆祝星战节&#xff0c;其口号是绝地武士的祝福语”愿原力Force和你同在“。虽然大多数 Li…

linux的manual手册不存在,在 Linux 命令行下使用“原力”

原标题&#xff1a;在 Linux 命令行下使用“原力” 和绝地武士的原力一样&#xff0c;-f 参数是很强大的&#xff0c;并伴随着潜在的毁灭性&#xff0c;在你能用好的时候又很便利。 -- Alan Formy-duval(作者) 近些年来&#xff0c;科幻发烧友开始在每年的 5 月 4 日庆祝 星战节…

动作捕捉软件系统有那么重要吗?

很多人会问&#xff0c;科技产品到底是硬件重要还是软件重要&#xff1f;这个真的不好回答&#xff0c;软件和硬件是一样的。有人说“硬件不够&#xff0c;软件来凑”&#xff0c;但是很多软件系统对硬件要求也很高&#xff0c;不然运行不起来。 比如动作捕捉相机&#xff0c;…

理解这7点就能掌握 JS 中的 this 指向

相信我&#xff0c;只要记住本文的 7️⃣ 步口诀&#xff0c;就能彻底掌握 JS 中的 this 指向。先念口诀&#xff1a;箭头函数、new、bind、apply 和 call、欧比届点&#xff08;obj.&#xff09;、直接调用、不在函数里。按照口诀的顺序&#xff0c;只要满足前面某个场景&…

js 原型相关知识点总结

一、相关资料 JS原型链与继承别再被问倒了js构造函数详解一步步图解javascript的原型(prototype)对象,原型链进阶必读&#xff1a;深入理解 JavaScript 原型JS原型链简单图解JS 中 this 指向问题通俗易懂之JavaScript手动实现apply方法 二、技术点 1.构造函数&#xff08;co…

JS 中 this 指向问题

相信我&#xff0c;只要记住本文的 7️⃣ 步口诀&#xff0c;就能彻底掌握 JS 中的 this 指向。 先念口诀&#xff1a;箭头函数、new、bind、apply 和 call、欧比届点&#xff08;obj.&#xff09;、直接调用、不在函数里。 按照口诀的顺序&#xff0c;只要满足前面某个场景&…

【历史上的今天】11 月 7 日:图灵奖女性得主诞生;Twitter 告别 140 字符时代;首位中国 AI 主播

整理 | 王启隆 透过「历史上的今天」&#xff0c;从过去看未来&#xff0c;从现在亦可以改变未来。 今天是 2021 年 11 月 7 日&#xff0c;在 1867 年的今天&#xff0c;居里夫人诞生&#xff1b;居里夫人是法国的著名科学家&#xff0c;研究放射性现象&#xff0c;发现镭和钋…

程序员要学会读源代码

原文作者&#xff1a;Jeff Atwood 在“沟通”这个复杂的领域里&#xff0c;写出能让人类领会并理解的连贯段落比敲出几行让解释器或编译器不致于“呕吐”的软件代码要难得多。 这就是为什么——就软件开发而言——所有的文档大概都是很差劲的。而且&#xff0c;由于为人写作比…