1.反射(Relection) 理解
定义:程序运行状态,动态地获取程序信息及调用程序功能即为java反射机制
2.获取class对象 掌握
2.1 Java代码的3个阶段
Java代码在计算机中经历的三个阶段:Source源代码阶段-Class类对象阶段-Runtime运行阶段
2.1.1 源代码(source):不是.java文件;而是编译器后生成的.class文件。.class文件包含了原始文件中所有信息
2.1.2 类对象(class):字节码文件(.class)被类加载器加载到虚拟机中,可通过".class"文件获取原始类中的所有信息,随之被加载的类就生成了一个Class类对象
2.1.3 运行时(runtime):通过new关键字创建对象(以类为模板创建对象),或使用Class对象的newInstance()方法创建对象
2.2 获取Class对象的原因
字节码被加载进入虚拟机后,随之加载的类就生成了一个Class对象,获取Class对象就可获取关于类的属性、方法和构造方法等信息,同时Class对象也是java反射机制的起源和入口
2.3 获取Class对象的方式
2.3.1 已知具体的类,通过类的class属性获取,此方法最安全可靠,程序性能最高
例如 Class<User> clazz = User.class;
2.3.2 已知某个类的具体实例,可调用该实例的getClass()方法获取Class对象
例如:User user = new User(); Class<? extends User> clazz = user.getClass();
2.3.3 已知类的全名,通过Class类的静态方法forName(String className)获取,但是可能会抛出ClassNotFoundException
例如: Class<?> clazz = Class.forName("com.xx.entity.User");
2.4 Class可以指那些结构
java语言中,Class可以指任意Java类型。通过class属性,可以获得基本数据类型、void、数组、枚举、接口、泛型和类等类型的Class对象。
2.5 关于Class对象总结
通过.class属性获取、通过对象的getCLss()方法获取、通过forName获取、通过类加载器获取
以上4种方式获取的Class对象的哈希值一模一样
-- 通过类加载器获取Class对象
ClassLoader classloader = ClassLoader.getSystemClassLoder();
Class<?> clazz = classloader.loadClass("类的完整路径");
Sout(clazz.hasCode);
3.类的加载过程 理解
3.1 加载类分析
字节码文件需加载到虚拟机中使用。
加载字节码文件三步骤:装载、连接、初始化
3.1.1 装载(loading) 类的class文件读入内存,并创建一个java.lang.Class对象,由类加载器完成
3.1.2 连接(linking)
3.1.2.1 验证(Verify) 确保加载类的信息复合JVM规范
3.1.2.2 准备(Prepare) 正式为静态变量在方法区中开辟存储空间并设置默认值。
3.1.2.3 解析(Resolve) 将虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程
3.1.3 初始化(initialization) 执行类构造器<clinit>()方法的过程,也就是把编译时期自动收集类中所有静态变量的赋值动作和静态代码块中的赋值语句合并。
<clinit> ()方法对于类或接口来说并不是必须的,如果一个类中没有静态语句块,也没有对变量的赋值操作,那么编译器可以不为这个类生成clinit()方法。
3.2 类的加载器分析
3.2.1 类加载器作用:将字节码文件加载到虚拟机内存
3.2.2 类加载器分类
启动类加载器(BootStrapClassLoader,也可称引导类加载器)
扩展类加载器(ExtensionClassLoader)
应用类加载器(SystemClassLoader,也称应用类加载器)
自定义类加载器(UserClassLoader)
-- 从启动类加载器到应用类加载器可通过getParent()方法分别获取上层的父加载器,但是三者之间不存在继承关系,而是属性关系
3.2.2.1 引导类加载器(BootStrap ClassLoader)
使用C/C++实现,嵌套在JVM内部,通过Java代码无法获得
用于加载Java核心库,用于提供JVM自身需要的类
并非继承java.lang.ClassLoader,没有父加载器
出于安全考虑,Bootstrap加载器只加载java,javax,sun开头的类
3.2.2.2 扩展类加载器(Expression ClassLoader)
由java语言编写,继承自ClassLoader类
父加载器为启动类加载器
负责加载从JDK安装目录jre/lib/ext子目录下加载类库
3.2.2.3 应用类加载器(System ClassLoader)
java语言编写,继承自ClassLoader类
父加载器为扩展类加载器
负责加载环境变量classpath或系统属性java.class.path指定路径下的类(加载自定义类)
3.2.2.4 用户自定义类加载器(User ClassLoader)
自定义类加载通过需要继承ClassLoader,父加载器为系统加载器
3.2.3 双亲委派模型
某个特定的类加载器接收到加载类的请求时,首先将加载任务委托给父类加载器,父类加载器又将任务向上委托,直到最父类加载器,
如果父类加载器可以完成类加载任务,就成功返回,如果不行就向下委托,由子类加载器进行加载。
双亲委派可避免类的重复加载,确保类的唯一性,同时保护程序安全,防止核心API被改写。
4.通过反射创建对象 掌握
4.1 newInstance()方法
可通过获取的Class对象来调用newInstance()方法,来实例化一个对象
通过Class对象调用newInstance()方法,默认会调用无参构造方法来创建对象
调用newInstance()方法,如果该类没有提供无参构造方法,则会抛出InstantiationException异常
如果该类的无参构造方法权限不够,则会抛出IllegalAccessException异常
由于该方法需要满足具备无参构造方法,并且该无参构造方法的权限需要足够,因为newInstance()方法具备很大的局限性。
开发中,想通过反射机制创建对象,一般都是通过Constructor类提供的方法实现。
4.2 使用反射的经典案例
使用new关键字创建对象,创建对象的代码是死的,每次只能创建1个对象
灵活而动态的创建对象,则可通过反射机制创建。例如将需要创建对象的类名写在配置文件中,读取配置文件中的类型然后再反射进行实例化,就可实现不编辑代码实现灵活创建。
5.获得类的所有结构 了解
5.1 获取类的相关信息
String getName(); --获取带包名的类型
String getSimpleName(); --获取不带包名的类型
Package getPackage(); --获取类的包对应的对象
Class<?> getSupperClass(); --获取类的父类对象
Class<?>[] getInterfaces(); --获取类实现的接口
5.2 获取类的所有属性
Field[] getFields(); --获取当前类和父类公开的属性
Field[] getDeclaredFields(); --获取当前类所有权限的属性
Field类常见方法:
String getName(); -- 获取属性名
Class<?> Type(); --获取属性类型
int getModifiers(); --获取属性修饰符,返回类型为数字,通过Modifier.toString将数组类型转换为字符串类型的修饰符
5.3 获取类的所有方法
Method[] getMethods(); --获取当前类和父类公开的方法
Method[] getDeclaredMethods(); --获取当前类所有权限的方法
Method类常见方法:
int getModifiers(); --获取属性修饰符,返回类型为数字,通过Modifier.toString将数组类型转换为字符串类型的修饰符
Class<?> getReturnType --以Class类型方式获取返回值类型
String getName(); --获取方法名
Class<?>[] getParameterTypes(); --获得方法形参类型
5.4 获取类的所有构造方法
Constructor[] getConstructors(); --获取当前类公开的构造方法
Constructor[] getDeclaredConstructors(); --获取当前类所有权限的构造方法
Constructor类常用方法:
int getModifiers(); --获取属性修饰符,返回类型为数字,通过Modifier.toString将数组类型转换为字符串类型的修饰符
String getName(); --获取方法名
Class<?>[] getParameterTypes(); --获取方法的形参类型
6.操作类的所有结构 重点
6.1 操作类中的属性(成员变量、静态变量)
获得属性的Field对象方法:
获得某个公开权限的属性(包含父类):public Field getField(String name)
获得当前类的某个属性(任意权限,常用于获取私有权限):public Filed getDeclaredField(String name)
操作属性的方法如下:
获得Obj对象中对应的属性值:public void get(Object obj)
设置obj对象中对应的属性值:public void set(Object obj,Object value)
参数值为true,代表允许操作私有属性:public void setAccessible(boolean isflag)
判断两个Field对象是否相等:public boolean equals(Object obj)
实例-操作公开权限的成员变量
获得Triger类的Class对象:Class<?> clazz = Class.forName("com.xx.entity.Triger");
获得Triger类中公开的name属性: Field field = clazz.getField("name");
通过反射机制来实例化Triger对象:Object obj = clazz.newInstance();
给name成员变量赋值:field.set(obj,"jack");
获得name成员变量值:String name = (String) field.get(obj);
实例2-操作私有权限的成员变量(仅显示和操作公开权限的成员变量的不同点)
获取Triger类中私有的color属性: Field colorFiled = clazz.getDeclaredField("color");
设置允许操作私有属性:colorField.setAccessible(true);
实例3-操作私有权限的静态变量(无需明确静态变量属于那个对象,操作静态变量绑定的对象设置为null即可)
获得Triger类的class对象:Class<?> clazz = Class.forName("com.xx.entity.Triger");
获取Triger类中私有的color属性:Field colorFiled = clazz.getDeclaredField("color");
设置允许操作私有数量的成员变量:colorField.setAccessible(true);
给colorFiled静态变量赋值:colorField.set(null,"black");
获取colorField静态变量值:String color = (String) colorField.get(null);
6.2 操作类中的方法
获得方法的Method对象方法:
获得公开权限的方法(包含父类):public Method getMethod(String name,Class<?>... parameterTypes) name为方法名,parameterTypes为形参类型
获得当前类的某个方法(任意权限):public Method getDeclaredMethod(String name,Class<?>... parameterTypes)
操作方法的方式如下:
调用某个方法:public Object incoke(Object obj,Object...args) obj表示绑定的对象,args表示传递的实参值.返回值为调用方法返回的结果,返回结果为null,表示无返回值
参数值为true,代表可调用私有方法:public void setAccessible(boolean flag)
实例:调用公开权限的成员方法
获得User类的Class对象:Class<?> calzz = Class.forName("com.xx.entity.User");
获得User类的公开的show(String)方法:Method showMethod = clazz.getMethod("show",String.class);
通过反射机制实例化User对象:Object obj = clazz.newInstance();
调用show方法:Object result = showMethod.invoke(obj,"小明");如果value为null,表示该方法无返回值
实例2-调用私有权限的成员方法
获取User类的Class对象:Class<?> clazz = Class.forName("com.xx.entity.User");
获取User类的私有的look(String,int)方法:Method lookMethod = clazz.getDeclaredMethod("look",String.class,int.class);
通过反射机制实例化User对象:Object obj = clazz.newInstance();
设置私有look方法可操作的权限:lookMethod.setAccessible(true);
调用look方法:Object value = lookMethod.invoke(obj,"小明",13); 如果value为null,表示该方法无返回值
实例3-调用私有权限的静态方法(无需明确静态方法属于那个对象,调用静态方法绑定的对象设置为Null即可)
获取User类的Class对象:Class<?> clazz = User.class;
获取User类的私有的sleep(String)静态方法:Method sleepMethod = clazz.getDeclaredMethod("sleep",String.class);
设置私有静态方法可调用:sleepMethod.setAccessible(true);
调用sleep静态方法,并获取返回值:Object value = sleepMethod.inovke(null,"小明"); 如果invoke方法的返回值为null,表示该方法无返回值
6.3 操作类中的构造方法
获得构造方法的Constructor对象方法:
获取公开权限的构造方法:public Constructor<?> getConstructor(Class<?>...parameterTypes) 参数为构造方法的形参类型
获取任意权限的构造方法:public Constructor<?> getDeclaredConstructor(Class<?>...parameterTypes) 参数为构造方法的形参类型
调用指定的构造方法创建对象,形参为构造方法需要传递的参数:public T newInstance(Object...initargs)
参数值为true,代表可以操作私有构造方法:public void setAccessible(boolean flag)
实例-通过公开权限的构造方法来创建对象
获取User类的class对象:Class<?> clazz = Class.forName("com.xx.entity.User");
获取公开权限的有参构造方法:Constructor<?> con = clazz.getConstructor(String.class);
调用有参构造方法来创建对象:User user = (User)con.newInstance("小明");
实例2-通过私有权限的构造方法来创建对象
获取User类的class对象:Class<?> clazz = Class.forName("com.xx.entity.User");
获取私有权限的有参构造方法:Constructor<?> con = clazz.getDeclaredConstructor(String.class,Double.class);
设置允许操作私有权限的构造方法:con.setAccessible(true);
调用有参构造方法来创建对象:User user = (User)con.newInstance("小明",19.0);
通过构造方法对象Constructor的newInstance创建对象比Class调用newInstance()方法灵活很多。常用Constructor创建对象
7.反射中泛型的使用 了解
8.反射的实战案例 掌握
8.1 榨果汁案例
需求:不改变程序代码的前提,创建出继承自Fruit的任意对象(Apple,Banana,Orange),然后调用榨汁机的方法开启榨汁操作
实现方案:定义抽象类Fruit,包含抽象方法squeeze;定义三个类都继承Fruit抽象类,重写squeeze方法;定义榨汁类,类中包含榨汁方法,参数为Fruit;
创建配置文件,定义完整的类名;测试类中读取配置文件类名,然后通过forName静态方法反射实例化对象,最后实例化榨汁类调用方法即可。
(想调用那个类的方法,修改配置文件中的类名即可)
8.2 模拟简单框架
配置文件中定义完整类名路径和类中的方法名
读取配置文件的完整类名和方法名
通过反射获取Class对象,然后通过Class对象获取构造方法和普通方法对象,再然后通过Constructor实例化对象
最后调用Method对象的invoke方法调用普通方法
JavaSE反射机制干货
2025/4/12 15:43:59
来源:https://blog.csdn.net/qq_48501521/article/details/147151600
浏览:
次
关键词:JavaSE反射机制干货
版权声明:
本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。
我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com
-
Stable Diffusion +双Contronet:从 ControlNet 边缘图到双条件融合:实现服装图像生成的技术演进——项目学习记录
-
RFID 在零售业的主要应用
-
每天学一个 Linux 命令(13):touch
热文排行
- 华为 海思22AP10(SS524)H.265 编解码处理器用户指南
- 数据库物理结构设计
- 基于重要抽样的主动学习不平衡分类方法ALIS
- 如何在 Mac 上清空硬盘后恢复丢失的数据?
- npm install puppeteer 报错 npm ERR! PUPPETEER_DOWNLOAD_HOST is deprecated解决办法
- 《缺失MRI模态下的脑肿瘤分割的潜在相关表示学习》| 文献速递-深度学习肿瘤自动分割
- (2)Django生产环境数据库的切换以及环境配置python-dotenv方案
- 概率图模型在自然语言处理中的应用
- 【微信小程序】自定义组件 - 组件的生命周期
- 大模型分离架构学习记录