当前位置: 首页 > news >正文

类和反射的机制

一、类

1.类的生命周期

1. 类的编译:通过 javac 命令将 .java 源文件编译成 .class 字节码文件。
2. 类的加载:类加载器(ClassLoader)将 .class 文件从硬盘加载到内存,形成“类对象”,包括加载、链接、初始化三个阶段。
3. 类的初始化和实例化:通过 new 关键字实例化类,创建具体对象。
4. 对象的生命周期:对象从创建到被垃圾回收器(GC)回收的整个过程。
5. 反射机制:允许在运行时动态加载类,扩展了类加载的灵活性。

二、 反射机制

Java 反射是指在运行时动态获取类的信息(如类名、方法、字段等)并操作类的能力。

三、反射的前提:获取 “类对象”

反射的所有操作,都必须基于一个核心载体 ——Class 类的对象(简称 “类对象”)。每个类在 JVM 中只会被加载一次,因此同一个类的类对象全局唯一。

1. 获取 Class 对象的三种方式

三种获取  Person  类的  Class  对象的方法

public class Test {public static void main(String[] args) throws ClassNotFoundException {// 1、硬盘阶段获取类对象Class class1 = Class.forName("com.qcby.reflect.Person");// 2、类阶段Class class2 = Person.class;// 3、对象阶段Person person = new Person();Class class3 = person.getClass();System.out.println(class1 == class2);System.out.println(class2 == class3);}
}

2.获取成员变量


2.1.读写变量值:set () 与 get ()

语法:
写值:field.set(对象实例, 变量值)(为指定对象的该变量赋值)
读值:field.get(对象实例)(获取指定对象的该变量值)
关键注意:若变量是 private 私有权限,直接读写会抛出 IllegalAccessException,需先调用 field.setAccessible(true) 开启 “暴力反射”,强制跳过权限检查。

2.2.应用

Person person = new Person();
// 获取类对象
Class pclass = Class.forName("com.qcby.reflect.Person");// 通过类对象获取成员变量们
Field[] fields = pclass.getDeclaredFields();
for (Field item : fields) {System.out.println(item);
}Field field_age = pclass.getDeclaredField("age");
System.out.println(field_age);Field[] public_fields = pclass.getFields();
for (Field item : public_fields) {System.out.println(item);
}Field field_from = pclass.getField("from");
System.out.println(field_from);// 获取和设置
field_from.set(person, "中国");
field_age.setAccessible(true);
field_age.set(person, 20);System.out.println(field_age.get(person));
System.out.println(field_from.get(person));
System.out.println(person);
// 1. 创建 Person 对象实例
Person person = new Person();
// 2. 获取类对象
Class pClass = Class.forName("reflect.Person");// 3. 获取所有成员变量(含 private)
Field[] allFields = pClass.getDeclaredFields();
for (Field field : allFields) {System.out.println(field); // 输出:private java.lang.String reflect.Person.name、private int reflect.Person.age、public java.lang.String reflect.Person.from
}// 4. 获取指定私有变量 name(需暴力反射)
Field nameField = pClass.getDeclaredField("name");
nameField.setAccessible(true); // 开启暴力反射,突破 private 限制
nameField.set(person, "赵嘉成"); // 赋值
System.out.println(nameField.get(person)); // 取值,输出:赵嘉成// 5. 获取指定公共变量 from(无需暴力反射)
Field fromField = pClass.getDeclaredField("from");
fromField.set(person, "中国"); // 直接赋值
System.out.println(person); // 输出:Person [name=赵嘉成, age=0, from=中国]

3.获取成员方法

3.1.执行方法:invoke ()

语法:method.invoke(对象实例, 方法参数值...)

若方法是静态方法(static),对象实例可传 null;
若方法无参数,参数值部分可省略或传空数组;
若方法有返回值,invoke() 会返回该值(需强转)。

权限注意:私有方法需先调用 method.setAccessible(true) 开启暴力反射。

3.2.执行代码

// 获取所有方法(包括私有)
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {System.out.println(method);
}// 调用私有方法
Method runMethod = clazz.getDeclaredMethod("run");
runMethod.setAccessible(true);
runMethod.invoke(person); // 执行方法

4.获取构造方法

构造方法的反射操作,核心是 “获取构造器” 和 “创建对象”(替代 new 关键字),支持通过无参 / 有参构造器创建实例。

返回类型方法签名(含形参)获取范围典型用途
Constructor<?>[]getDeclaredConstructors()所有构造器(包括 private)暴力反射,想拿到任意访问修饰符的构造器
Constructor<T>getDeclaredConstructor(Class<?>... parameterTypes)指定一个构造器(包括 private)暴力反射,精确匹配形参列表
Constructor<?>[]getConstructors()仅 public 构造器正常反射,只关心公开构造器
Constructor<T>getConstructor(Class<?>... parameterTypes)指定一个 public 构造器正常反射,精确匹配形参列表且为 public

4.1 创建对象newInstance ()

语法:constructor.newInstance(构造参数值...)
无参构造器:参数值部分可省略,直接 constructor.newInstance();
有参构造器:需传入与参数列表匹配的参数值;
私有构造器:需先调用 constructor.setAccessible(true) 开启暴力反射。

// 1. 拿所有构造器(含 private)
Constructor<?>[] all = clazz.getDeclaredConstructors();// 2. 拿指定 private 构造器(如 Person(String, int))
Constructor<Person> c1 = clazz.getDeclaredConstructor(String.class, int.class);
c1.setAccessible(true);          // 暴力反射
Person p1 = c1.newInstance("Tom", 20);// 3. 拿所有 public 构造器
Constructor<?>[] pub = clazz.getConstructors();// 4. 拿指定 public 无参构造器
Constructor<Person> c2 = clazz.getConstructor();
Person p2 = c2.newInstance();

四、其他配置

1.步骤拆解与代码

创建配置文件(refconfig.properties):

# 配置要加载的类(全类名)
reflect.className=reflect.Person
# 配置要调用的方法名
reflect.methodName=getAge

2.获取类的基本信息

需求API示例结果
全限定名(包 + 类)clazz.getName()com.qcby.reflect.Person
简单类名clazz.getSimpleName()Person
包名clazz.getPackage().getName()com.qcby.reflect
父类clazz.getSuperclass()class java.lang.Object
实现的接口数组clazz.getInterfaces()[interface java.io.Serializable]
修饰符(public/private/…)Modifier.toString(clazz.getModifiers())public
是否为接口clazz.isInterface()FALSE
是否为数组clazz.isArray()FALSE
是否为枚举clazz.isEnum()FALSE
是否为注解clazz.isAnnotation()FALSE
是否为基本类型clazz.isPrimitive()FALSE

3.常见问题

​​1.反射能修改final字段吗?​​

可以,但需要先调用setAccessible(true)。

​​2.反射如何获取父类的方法?

​​使用getSuperclass()和getDeclaredMethods()。

3.​​反射为什么慢?​​

反射调用需要检查访问权限、动态解析方法名,无法被JIT优化。

http://www.dtcms.com/a/358441.html

相关文章:

  • hashmap计算key的hash的时候为什么要右移16位
  • 鸿蒙ArkTS 核心篇-16-循环渲染(组件)
  • Ruoyi-vue-plus-5.x第一篇Sa-Token权限认证体系深度解析:1.3 权限控制与注解使用
  • 【计算机组成原理】LRU计数器问题
  • Vue3 + GeoScene 地图点击事件系统设计
  • Selenium + PO 框架进阶实践:接入 Allure 报告与 Jenkins 持续集成
  • macOs上ffmpeg带入libx264库交叉编译
  • docker 启动一个clickhouse , docker 创建ck数据库
  • Python远程文件管理移动端适配与跨平台优化实战
  • vue3多个el-checkbox勾选框设置必选一个
  • 【OpenGL ES】光栅化插值原理和射线拾取原理
  • Day17(前端:JavaScript基础阶段)
  • Cocos游戏中自定义按钮组件(BtnEventComponent)的详细分析与实现
  • HAProxy 负载均衡全解析:从基础部署、负载策略到会话保持及性能优化指南
  • Spring : 事务管理
  • 音视频学习(六十一):H265中的VPS
  • Prompt Engineering:高效构建智能文本生成的策略与实践
  • 深层语义在自然语言处理中的理论框架与技术融合研究
  • AI大模型:(二)5.2 文生视频(Text-to-Video)模型训练实践
  • FPGA增量式方差与均值计算
  • 响应式编程框架Reactor【4】
  • FPGA学习笔记——SPI读写FLASH
  • 优化器全指南:从原理到调优实战
  • 原子操作与锁实现
  • 由于不对称GND过孔配置,差分信号过孔上的差模到共模转换
  • SQL相关知识 CTF SQL注入做题方法总结
  • seafile-setup-troubleshooting_# Seafile 安装与问题解决记录 # Seafile/Seahub 启动问题记录文档
  • Scikit-learn Python机器学习 - Scikit-learn加载数据集
  • C/C++:AddressSanitizer内存检测工具
  • 《以奋斗者为本》读书笔记(上篇:价值管理)