java反射
反射机制:
Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API 取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
加载完类之后,在内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个 对象就包含了完整的类的结构信息。
我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透 过这个镜子看 到类的结构,所以,我们形象的称之为:反射。
Java反射机制提供的功能:
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时获取泛型信息
- 在运行时调用任意一个对象的成员变量和方法
- 在运行时处理注解
- 生成动态代理
反射相关的主要API:
- java.lang.Class :代表一个类
- java.lang.reflect.Method :代表类的方法
- java.lang.reflect.Field :代表类的成员变量
- java.lang.reflect.Constructor :代表类的构造器
Class类
在Object类中定义了以下的方法,此方法将被所有子类继承: public final Class getClass() 方 法返回值的类型是一个Class类,此类是Java反射的源头,实际上所谓反射从程序的运行结果来看也很好 理解,即:可以通过对象反射求出类的名称。
对象通过反射后可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了哪些接口。
对于每 个类而言,JRE 都为其保留一个不变的 Class 类型的对象。
一个 Class 对象包含 了特定某个结构 (class/interface/enum/annotation/primitive type/void/[])的有关信息。
- Class本身也是一个类
- Class对象只能由系统建立对象
- 一个加载的类在 JVM 中只会有一个Class实例
- 一个Class对象对应的是一个加载到JVM中的一个 .class 文件
- 每个类的实例都会记得自己是由哪个 Class 实例所生成
- 通过Class可以完整地得到一个类中的所有被加载的结构
- Class类的对象是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象
常用方法:
import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; /** * @program: FirstDemo * @description: Java反射学习 * @author: GuoTong * @create: 2020-08-25 15:47 **/ @SuppressWarnings("all") public class TestReflection { public static void main(String[] args) throws Exception { //获取 Person 的字节码对象,由Java虚拟机由Class创建出来的,描述 Person 类结构的对象。 //方式一: Person person = new Person("郭童", 23); System.out.println("反射之后" + person); //方式二 Class<?> person2 = Class.forName("Person"); Object obj1 = person2.newInstance(); //方式三: Constructor<?> declaredConstructor1 = person2.getDeclaredConstructor(); Object obj2 = declaredConstructor1.newInstance(); //方式四 Class<? extends Person> personClass = person.getClass(); //getDeclaredFields||getDeclaredField;获取当前类的 //getFile|| getFiles 是获得能够根据权限修饰符拿到的数据。可以获取穿透。 //获取里面的属性(成员变量) /* Field[] declaredFields = personClass.getDeclaredFields(); for (Field field : declaredFields) { System.out.println(field); }*/ //操作成员变量: Field name = personClass.getDeclaredField("name"); //成员变量赋值的时候,set的时候需要绑定当前对象。另一个参数为值。 // name.set(person,"李四");//不过这里会报错,private修饰的。不能这样去改变 // Class TestReflection can not access a member of class Person with modifiers "privat //暴力设置 name.setAccessible(true);//private修饰的。暴力访问 name.set(person, "李四"); System.out.println("反射之后" + person); System.out.println("获取所有的方法:------------------不可穿透型"); //获取所有的方法 Method[] declaredMethods = personClass.getDeclaredMethods(); for (Method method : declaredMethods) { System.out.println(method); } //得到需要被调用方法 Method sayChinese = personClass.getDeclaredMethod("sayChinese"); sayChinese.invoke(person);//调用方法 //带参数的,拿的话需要定制形参类型。 Method sayChinese2 = personClass.getDeclaredMethod("sayChineseDe", String.class); sayChinese2.invoke(person, "李四");//调用方法 //private修饰的方法也可以暴力访问 //method.setAccessible(true); System.out.println("获取构造器------------------"); //构造器 Constructor<?>[] declaredConstructors = personClass.getDeclaredConstructors(); for (Constructor<?> constructor : declaredConstructors) { System.out.println(constructor); } //获取带参数的构造器 Constructor<? extends Person> declaredConstructor = personClass.getDeclaredConstructor(String.class, int.class); System.out.println(declaredConstructor); //利用拿到的构造器创建对象 Person person1 = declaredConstructor.newInstance("网吧", 32); System.out.println(person1); } } @SuppressWarnings("all") class Person { private String name; private int age; public Person() { } public Person(String name, int age) { this.name = name; this.age = age; } public void sayChinese() { System.out.println("说中文"); } public void sayChineseDe(String name) { System.out.println("说中文" + name); } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } 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; } }
类的加载过程 当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过如下三个步骤来对该类进行初 始化。
1. 加载:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结 构,然后生成一个代表这个类的 java.lang.Class 对象,作为方法区中类数据的访问入口(即引 用地址)。所有需要访问和使用类数据只能通过这个Class对象。这个加载的过程需要类加载器参 与。
2. 链接:将Java类的二进制代码合并到JVM的运行状态之中的过程。 验证:确保加载的类信息符合JVM规范,例如:以cafe开头,没有安全方面的问题 准备:正式为类变量(static)分配内存并设置类变量默认初始值的阶段,这些内存 都将在 方法区中进行分配。 解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。
3. 初始化: 执行类构造器()方法的过程。类构造器()方法是由编译期自动收集类中 所有类变量的赋值动作 和静态代码块中的语句合并产生的。(类构造器是构造类信 息的,不是构造该类对象的构造 器)。 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类 的初始 化。 虚拟机会保证一个类的()方法在多线程环境中被正确加锁和同步。
/** * @program: FirstDemo * @description: 类加载器 * @author: GuoTong * @create: 2020-08-25 17:02 **/ public class ClassLoaderTest { public static void main(String[] args) throws ClassNotFoundException { //1.获取一个系统类加载器 ClassLoader classloader = ClassLoader.getSystemClassLoader(); System.out.println(classloader); //2.获取系统类加载器的父类加载器,即扩展类加载器 classloader = classloader.getParent(); System.out.println(classloader); //3.获取扩展类加载器的父类加载器,即引导类加载器 classloader = classloader.getParent(); System.out.println(classloader);//null,为什么:因为它是由C++编写的 //打印为null,是jvm提示我们,前面已经到了java世界的尽头了。 //4.测试当前类由哪个类加载器进行加载 classloader = Class.forName("ClassLoaderTest").getClassLoader(); System.out.println(classloader); } }
个性签名:独学而无友,则孤陋而寡闻。做一个灵魂有趣的人!
如果觉得这篇文章对你有小小的帮助的话,记得在右下角点个“推荐”哦,博主在此感谢!
Java入门到入坟
万水千山总是情,打赏一分行不行,所以如果你心情还比较高兴,也是可以扫码打赏博主,哈哈哈(っ•̀ω•́)っ✎⁾⁾!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南