反射 Reflection

反射

反射的概念

  1. 反射机制允许程序在执行期借助于ReflectionAPI取得任何类的内部信息(比如成员变量,构造器,成员方法等等),并能操作对象的属性及方法。反射在设计模式和框架底层都会用到
  2. 加载完类之后,在堆中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象包含了类的完整结构信息。通过这个对象得到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,形象的称之为反射

image.png

反射机制

Java反射机制可以完成

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

反射相关的主要类:

  1. java.lang.Class:代表一个类,Class对象表示某个类加载后在堆中的对象
  2. java.lang.reflect.Method: 代表类的方法
  3. java.lang.reflect.Field: 代表类的成员变量
  4. java.lang.reflect.Constructor: 代表类的构造方法

一般使用:对象.方法 反射使用:方法.对象
一般使用:对象.变量 反射使用:变量.对象

反射优点和缺点

**优点:**可以动态的创建和使用对象(也是框架底层核心),使用灵活,没有反射机制,框架技术就失去底层支撑。
**缺点:**使用反射基本是解释执行,对执行速度有影响,

反射调用优化-关闭访问检查

Method和Field、Constructor对象都有setAccessible()方法
setAccessible作用是启动和禁用访问安全检查的开关。(默认为false)

  • 参数值为true表示:反射的对象在使用时取消访问检查,提高反射的效率。
  • 参数值为false表示:反射的对象执行访问检查

Class类

  1. Class也是类,因此也继承Object类

image.png

  1. Class类对象不是new出来的,而是系统创建的
  2. 对于某个类的Class类对象,在内存中只有一份,因为类只加载一次(以hashcode区别)(使用synchronized同步)
  3. 每个类的实例都会记得自己是由哪个 Class 实例所生成
  4. 通过Class对象可以完整地得到一个类的完整结构,通过一系列API
  5. Class对象是存放在堆的
  6. 类的字节码二进制数据,是放在方法区的, 有的地方称为类的元数据(包括 方法代码变量名,方法名,访问权限等等)https://www.zhihu.com/question/38496907

Class类的常用方法:

java.lang.Class

  1. getName:获取全类名
  2. getSimpleName:获取简单类名
  3. getFields:获取所有public修饰的属性,包含本类以及父类的
  4. getDeclaredFields:获取本类中所有属性
  5. getMethods:获取所有public修饰的方法,包含本类以及父类的
  6. getDeclaredMethods:获取本类中所有方法
  7. getConstructors: 获取本类所有public修饰的构造器
  8. getDeclaredConstructors:获取本类中所有构造器
  9. getPackage:以Package形式返回 包信息
  10. getsuperClass:以Class形式返回父类信息
  11. getlnterfaces:以Class[]形式返回接口信息
  12. getAnnotations:以Annotation[] 形式返回注解信息

获取Class对象的几种方式

  1. 代码阶段:Class.forName()

前提:已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,可能抛出ClassNotFoundException,
例:Class clazz = Class.forName( "java.lang.Cat”);
应用场景:多用于配置文件,读取类全路径,加载类

  1. Class类阶段:类.class

前提:若已知具体的类,通过类的class 获取,该方式最为安全可靠,程序性能最高
应用场景:多用于参数传递,比如通过反射得到对应构造器对象

  1. Runtime阶段:对象.getClass()

前提:已知某个类的实例,调用该实例的getClass()方法获取Class对象,
例:Class clazz = object.qetClass();
应用场景:通过创建好的对象,获取Class对象

  1. 其他方式(类加载器):

ClassLoader classLoader = object.getClass().getClassLoader();
Class clazz = classLoader.loadClass("类的全类名");

  1. 基本数据(int, char,boolean,float,double,byte,long,short) 按如下方式得到Class类对象

Class clazz = 基本数据类型.class;

  1. 基本数据类型对应的包装类,可以通过.type 得到Class类对象

Class clazz = 包装类.TYPE;

以下类型有Class对象

  1. 外部类,成员内部类,静态内部类,局部内部类,匿名内部类
  2. interface:接口
  3. 数组
  4. enum:枚举
  5. annotation:注解
  6. 最本基本数据类型
  7. void

类加载

基本说明

反射机制是java实现动态语言的关键,也就是通过反射实现类动态加载。

  1. 静态加载:编译时加载相关的类,如果没有则报错,依赖性太强
  2. 动态加载:运行时加载需要的类,如果运行时不用该类,则不报错,降低了依赖性

类加载时机

  1. 创建类的实例:属于静态加载。当使用 new 关键字创建一个类的实例时,该类会被加载、连接和初始化,这是在编译期就确定需要加载的类。
  2. 访问类的静态变量:属于静态加载。当访问一个类的静态变量(static field)时,如果该类还没有加载,则会触发类的加载、连接和初始化,这也是在编译期就确定需要加载的类。
  3. 调用类的静态方法:属于静态加载。当调用一个类的静态方法时,如果该类还没有加载,则会触发类的加载、连接和初始化,同样是在编译期确定需要加载的类。
  4. 访问类的静态字段的值:属于静态加载。当访问一个类的静态字段的值(static final field)时,不会触发类的初始化,因为这些字段在编译期就会被赋值。
  5. 使用反射:属于动态加载。通过反射机制来创建类的实例、访问类的静态变量或调用类的静态方法时,是在程序运行时根据需要动态加载类。

类加载过程

image.png
类加载各阶段完成任务
image.png
前两个阶段在JVM中

加载阶段(Loading)

加载阶段是指查找并加载类的字节码文件(.class 文件),一般是通过类加载器(ClassLoader)来完成的。类加载器根据类的全限定名在类路径中查找对应的字节码文件,并将其加载到内存中。

链接阶段(Linking)-验证

验证确保被加载的类符合 Java 虚拟机规范,比如检查字节码的格式、语义等。
目的是为了确保 Class 文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
包括:文件格式验证(是否以魔数 oxçafebabe开头)、元数据验证、字节码验证和符号引用验证。
可以考虑使用 -Xverify:none 参数来关闭大部分的类验证措施,缩短虚拟机类加载的时间

链接阶段(Linking)-准备

JVM 会在该阶段对静态变量,分配内存并默认初始化(对应数据类型的默认初始值如 0、0L、null、false等)。这些变量所使用的内存都将在方法区中进行分配

链接阶段(Linking)-解析

虚拟机将常量池内的符号引用转换为直接引用,比如将类、方法、字段等的符号引用解析为实际内存地址

初始化(Initialization)

到初始化阶段,才真正开始执行类中定义的 Java 程序代码,此阶段是执行()方法的过程。
()方法是由编译器按语句在源文件中出现的顺序,依次自动收集类中的所有静态变量的赋值动作和静态代码块中的语句,并进行合并。类初始化是按需进行的,只有在首次主动使用类时才会触发。
虚拟机会保证一个类的()方法在多线程环境中被正确地加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的()方法,其他线程都需要阻塞等待,直到活动线程执行()方法完毕

通过反射获取类的结构

通过反射创建对象

  1. 方式一:调用类中的public修饰的无参构造器
  2. 方式二:调用类中的指定构造器
  3. Class类相关方法
    1. newlnstance:调用类中的无参构造器,获取对应类的对象
    2. getConstructor(Class…clazz):根据参数列表,获取对应的构造器对象
    3. getDecalaredConstructor(Class…clazz):根据参数列表,获取对应的构造器对象
  4. Constructor类相关方法
    1. setAccessible():参数使用true:暴破,使用反射可以访问私有(private)构造器/属性/方法
    2. newlnstance(Object…obj):调用构造器
package com.gg
class abc{
    puiblic String name ;
    abc(String name){
        this.name = name ;
    }
}
Class<?> clazz = Class.forName("com.gg.abc");
Object o = clazz.newInstance(); // 调用无参构造 o的运行对象类型为abc类

Constructor<?>  c = clazz.getDecalaredConstructor(String.class);//调用有参构造
Object o1 = c.newInstance("gggg"); // o1的运行对象类型为abc类

通过反射访问类中的成员

  1. 根据方法名和参数列表获取Method方法对象:Method m =clazz.getDeclaredMethod(方法名,XX.class);
  2. 获取对象:Object o = clazz.newInstance();
  3. 暴破:m.setAccessible(true);
  4. 访问:Object returnValue = m.invoke(o,实参列表); //o表示对象
  5. 注意:如果是静态方法,则invoke的参数o,可以写成null
  6. 如果方法有返回值,统一返回Object,但其运行类型依旧是方法中定义的返回类型
package com.gg
class abc{
    puiblic String name ;
    abc(String name){
        this.name = name ;
    }
    public void aabc(String name){
        System.out.println(name + " " + this.name);
    }
}
// 获取Class对象
Class<?> clazz = Class.forName("com.gg.abc");
//调用有参构造
Constructor<?>  c = clazz.getDecalaredConstructor(String.class);
// o1的运行对象类型为abc类
Object o1 = c.newInstance("gggg"); 
// 获取aabc方法对象
Method m = clazz.getDeclaredMethod("aabc",String.class);
// 反射调用aabc方法
Object res = m.invoke(o1,"aaaa");
// 输出 "aaaa gggg"

通过反射访问类中的属性

  1. 根据属性名获取Field对象Field f = clazz.getDeclaredField(属性名);
  2. 暴破:
    1. f.setAccessible(true); // f 是Field
  3. 访问
    1. f.set(o,值);// o表示对象
    2. f.get(o); //o表示对象
  4. 注意:如果是静态属性,则set和get中的参数o,可以写成null
package com.gg
class abc{
    puiblic String name ;
    abc(String name){
        this.name = name ;
    }
    public void aabc(String name){
        System.out.println(name + " " + this.name);
    }
}
// 获取Class对象
Class<?> clazz = Class.forName("com.gg.abc");
//调用有参构造
Constructor<?>  c = clazz.getDecalaredConstructor(String.class);
// o1的运行对象类型为abc类
Object o1 = c.newInstance("gggg"); //此时name="gggg"
// 获取name属性对象
Field f = clazz.getDeclaredField("name");
// 设置name属性的值
f.set(o1,"123");// 此时name="123"
// 获取并输出name属性值
System.out.println(f.get(o1));
// 输出 "123"

Field类常用方法

java.lang.reflect.Field

  1. getModifiers:以int形式返回修饰符、
    1. [说明:默认修饰符 是0, public 是1,private 是2,protected 是 4,static是8,final是16]
    2. public static = public(1) + static(8) = 1 + 8 = 9
  2. getType:以Class形式返回类型
  3. getName:返回属性名

Method类常用方法

java.lang.reflect.Method

  1. getModifiers:以int形式返回修饰符
    1. [说明:默认修饰符 是0, public 是1,private 是2,protected 是 4,static是8,final是16]
  2. getReturnType:以Class形式获取 返回类型
  3. getName:返回方法名
  4. getParameterTypes:以Class[ ]返回参数类型数组

Constructor类常用方法

java.lang.reflect.Constructor

  1. getModifiers:以int形式返回修饰符
  2. getName:返回构造器名(全类名)
  3. getParameterTypes:以Class[ ]返回参数类型数组

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/470898.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SurfaceFlinger实战dump获取单个Layer图像方案学员改进成果

背景&#xff1a; hi&#xff0c;粉丝朋友们&#xff1a; 在马哥课程的实战实现dump单个图层的发布后&#xff0c;很多学员朋友就纷纷享马哥要了相关源码&#xff0c;相关的链接请参考这里&#xff1a; https://blog.csdn.net/learnframework/article/details/136323076 学员…

前端项目,个人笔记(三)【Vue-cli - api封装-axios使用举例】

目录 前言 1、axios配置与测试 1.1、配置 1.2、测试 2、使用axios案例-渲染header 3、Pinia优化重复请求 3.1、为什么&#xff1f; 3.2、使用Pinia优化代码步骤 步骤一&#xff1a;在main.js中创建 Pinia 实例&#xff0c;并将其作为插件添加到 Vue 应用中 步骤二&am…

【QT+QGIS跨平台编译】之八十四:【QGIS_Gui跨平台编译】—【错误处理:未实例化QgsMapLayer - QgsHighlight】

文章目录 一、未实例化QgsMapLayer二、错误处理 一、未实例化QgsMapLayer 报错信息&#xff1a; 二、错误处理 第31行修改为&#xff1a; #include "qgsmaplayer.h"

【Leetcode】2684. 矩阵中移动的最大次数

文章目录 题目思路代码结果 题目 题目链接&#x1f517; 给你一个下标从 0 开始、大小为 m x n 的矩阵 grid &#xff0c;矩阵由若干 正 整数组成。 你可以从矩阵第一列中的 任一 单元格出发&#xff0c;按以下方式遍历 grid &#xff1a; 从单元格 (row, col) 可以移动到 (…

Flutter-仿携程首页类型切换

效果 唠叨 闲来无事&#xff0c;不小心下载了携程app&#xff0c;还幻想可以去旅游一番&#xff0c;奈何自己运气不好&#xff0c;自从高考时第一次吹空调导致自己拉肚子考试&#xff0c;物理&#xff0c;数学考了一半就交卷&#xff0c;英语2B铅笔除了问题&#xff0c;导致原…

MySQL数据库的基本概念与安装

目录 引言 一、数据库的基本概念 &#xff08;一&#xff09;数据、表与数据库 1.数据(Data) 2.表 3.数据库 &#xff08;二&#xff09;数据库管理系统 &#xff08;三&#xff09;数据库系统 二、数据库的发展 三、主流数据库的介绍 &#xff08;一&#xff09;关…

一命通关深度优先遍历

前言 在解释深度优先遍历之前&#xff0c;我想先让大家去思考一个可能从未想过的问题&#xff1a; 为什么我们在学习基础数据结构的时候&#xff0c;都没有出现dfs和bfs这两个词&#xff0c;而在学习二叉树和图的时候&#xff0c;突然蹦出了深度优先遍历和广度优先遍历这两个…

力扣 第 126 场双周赛 解题报告 | 珂学家 | 贡献法思维场 + 贪心构造 + 0-1背包

前言 整体评价 T3是道好题&#xff0c;一开始思路偏了往按字母前缀和和DP去想了&#xff0c;但是感觉很难下手&#xff0c;后来发现从贡献的角度&#xff0c;其实和位子无关系&#xff0c;只需要贪心即可。 T4也是一道贡献思路题&#xff0c;理清核心的点&#xff0c;就能简单…

Vue项目使用process.env关键字及Vue.config.js配置解决前端跨域问题

1.process.env 是Node.js 中的一个环境 1.打开命令行查看环境: 2.process.env与Vue CLI 项目 Vue Cli 有以下三种运行模式 development 模式用于 vue-cli-service serve test 模式用于 vue-cli-service test:unit production 模式用于 vue-cli-service build 和 vue-cli-se…

每日一练:LeeCode-167. 两数之和 II - 输入有序数组【双指针】

给你一个下标从 1 开始的整数数组 numbers &#xff0c;该数组已按 非递减顺序排列 &#xff0c;请你从数组中找出满足相加之和等于目标数 target 的两个数。如果设这两个数分别是 numbers[index1] 和 numbers[index2] &#xff0c;则 1 < index1 < index2 < numbers.…

如何本地部署1Panel面板

文章目录 前言1. Linux 安装1Panel2. 安装cpolar内网穿透3. 配置1Panel公网访问地址4. 公网远程访问1Panel管理界面5. 固定1Panel公网地址 前言 1Panel 是一个现代化、开源的 Linux 服务器运维管理面板。高效管理,通过 Web 端轻松管理 Linux 服务器&#xff0c;包括主机监控、…

YOLOv8改进 | 主干篇 | 利用图像分割网络UNetV2改善图像分割检测性能(全网独家首发)

一、本文介绍 本文给大家带来的改进机制是利用图像分割网络UNetV2的主干来改进我们的YOLOv8分割模型&#xff08;本文的内容虽然YOLOv8所有的功能的用户都能使用&#xff0c;但是还是建议分割的用户使用&#xff09;&#xff0c;U-Net v2 旨在改进医学图像分割的性能&#xff…

C# 方法(函数)

文章目录 C# 方法&#xff08;函数&#xff09;简单示例程序代码运行效果 值传递和引用传递示例程序 运行效果按输出传递参数运行结果 C# 方法&#xff08;函数&#xff09; 简单示例 程序代码 访问的限制符 using System; using System.Collections.Generic; using Syste…

GO-初识包管理

初识包管理&#xff0c;知道项目中文件和文件夹之间的关系 输出&#xff0c;代码&#xff0c;在go编译器运行时会显示在屏幕中 初识数据类型 整型&#xff0c;数字。例如&#xff1a;1、2、3、4 字符串类型&#xff0c;表示文本信息的。例如:“张三”“李四” 布尔类型&#x…

Day51-Nginx多实例知识与大厂企业级实战

Day51-Nginx多实例知识与大厂企业级实战 1. 什么是nginx多实例&#xff1f;2. 为什么要用多实例&#xff1f;3. 大厂数百个业务项目&#xff0c;如何来管理&#xff1f;4. 大厂上百项目web分用户解决方案4.1 编译nginx环境实践&#xff1a;4.2 zuma实例(利用普通用户权限将不同…

Arduino 开发 野火ESP8266模块的串口通信

一、硬件连接 Arduino ESP8266 串口通信是一个常见的任务&#xff0c;允许通过串行接口与其他设备或计算机进行通信。 连接硬件&#xff1a;将野火ESP8266模块通过USB连接到电脑。注意电源为3.3V。 选择开发板和端口&#xff0c;在Arduino IDE中&#xff0c;选择ESP8266开发板…

图神经网络实战(5)——常用图数据集

图神经网络实战&#xff08;5&#xff09;——常用图数据集 0. 前言0. 图数据集介绍2. Cora 数据集3. Facebook Page-Page 数据集小结系列链接 0. 前言 图数据集往往比单纯的连接集合更丰富&#xff0c;节点和边也可以具有表示分数、颜色、单词等的特征。在输入数据中包含这些…

NLP---Bert分词

目录&#xff1a; Q&#xff1a;bert分词步骤1&#xff1a;构建N * N 的相关性矩阵&#xff0c;计算相邻两个字的相关性&#xff0c;低的话&#xff08;<阈值&#xff09;就切割。2&#xff1a;将A词进行mask计算出A的embedding&#xff0c;然后将AB两个词一起mask&#xff…

【滑动窗口】长度最小的子数组|无重复字符的最长子串|最大连续1的个数 III|将 x 减到 0 的最小操作数

1. 长度最小的子数组 - 力扣&#xff08;LeetCode&#xff09; 1.题目解析&#xff1a; 2.算法原理 &#xff08;1&#xff09;方法一&#xff1a;暴力列举出所有的子数组的和 时间复杂度&#xff1a;O&#xff08;n**2&#xff09;&#xff1a;枚举所有子数组O&#xff08;…

使用 Redux 管理 React 应用状态

使用 Redux 管理 React 应用状态 在复杂的 React 应用中&#xff0c;管理组件状态变得越来越复杂&#xff0c;这时候引入 Redux 可以帮助我们更好地管理状态。Redux 是一个可预测状态容器&#xff0c;它可以帮助我们统一管理应用的状态&#xff0c;使得状态变化更加可控。本文…