Java反射
借鉴地址:https://blog.csdn.net/weixin_45395059/article/details/126765905?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522c39eb955699400d0d20107e80a739f5e%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=c39eb955699400d0d20107e80a739f5e&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2alltop_positive~default-1-126765905-null-null.142v102control&utm_term=java%E5%8F%8D%E5%B0%84%E6%9C%BA%E5%88%B6&spm=1018.2226.3001.4187
反射:Java在运行期间可以获取到一个对象的全部信息。
反射机制一般是用来解决Java程序运行期间,对某个实例对象一无所知的情况下,如何调用该对象内的方法问题。这种通过Class实例获取类class信息的方法称为反射(Reflection)
反射优点和缺点:
优点:可以动态地创建和使用对象,反射机制是 Java 框架的底层核心,其使用灵活,没有反射机制,底层框架就失去支撑。
缺点:使用反射基本是解释执行,对程序执行速度有影响。
反射常用的几种用法:1. 获取Class对象
2. 获取字段信息(获取字段四种方法
点击查看代码
Field getField(name):根据字段名获取某个 public 的 field(包括父类)
Field getDeclaredField(name):根据字段名获取当前类的某个 field(不包括父类)
Field[] getFields():获取所有 public 的 field(包括父类)
Field[] getDeclaredFields():获取当前类的所有 field(不包括父类)
初始化阶段:JVM 才会真正执行类中定义的 Java程序代码,此阶段是执行
类加载概述:在深入讲解反射前,先来介绍一下 Java中类的加载与反射机制。
反射机制是 Java实现动态语言的关键,也就是通过反射实现类的动态加载。
静态加载:编译时就加载相关的类,如果程序中不存在该类则编译报错,依赖性太强。
动态加载:运行时加载相关的类,即使程序中不存在该类,但如果运行时未使用到该类,也不会编译错误,依赖性较弱。
而类class是由 JVM 在执行过程中动态加载的。JVM在第一次读取到一种类class时,会将其加载进内存。
Class类的构造方法是private,即只有 JVM 能创建Class类对象,我们程序员自己的 Java 程序是无法创建Class类对象的。
每加载一种class,JVM就为其创建一个Class类的对象,并将两者关联起来。注意:这里的Class类是一个名字叫Class的类class。它长这样:
public final class Class { private Class() {} }
如何获取一个class的Class实例?有5个方法:
方法一:直接通过一个类class中的静态变量class获取:
Class cls = String.class;// class 是 String 类中的一个静态变量
方法二:如果我们有一个类class的对象,可以通过该对象引用提供的getClass()方法获取:
String s = "Hello"; Class cls = s.getClass();// 调用 String类对象 s的 getClass() 方法获取
方法三:如果知道一个类class的完整类名,可以通过Class类的静态方法Class.forName()获取:
Class cls = Class.forName("java.lang.String");// java.lang.String 是 String 类的完整类名
方法四:对于基本数据类型(int、char、boolean、float 等),通过 基本数据类型.class 获取:
Class integerClass = int.class; Class characterClass = char.class; Class booleanClass = boolean.class; System.out.println(integerClass);// int
方法五:对于基本数据类型对应的包装类,可以通过类中的静态变量TYPE获取到Class类对象:
Class type1 = Integer.TYPE; Class type2 = Character.TYPE; System.out.println(type1);// int
因为Class类对象在 JVM 中是唯一的,所以,上述方法获取的Class类对象是同一个对象。可以用==比较两个Class类对象(同一个地址)
instanceof 是一个用于检查对象类型的二元运算符,返回值:boolean(true 或 false),作用:检查左侧对象是否是右侧类/接口的实例。
因为反射的目的是为了获得某个类的实例对象的信息。因此,当我们拿到某个Object对象时,可以通过反射直接获取该Object的class信息,而不需要使用向下转型:
void printObjectInfo(Object obj) { Class cls = obj.getClass(); }
String s = "Hello"; 这一行代码的底层行为与字符串常量池(String Pool)密切相关,字符串常量池是JVM在方法区(Java 8之前)/ 堆内存(Java 8及之后)中维护的一块特殊内存区域。 首次执行 String s = "Hello",JVM会先在字符串常量池中查找是否存在值为 "Hello" 的字符串对象,如果已存在,直接复用常量池中的对象,不会创建新对象。如果不存在,在字符串常量池中新建一个 String 对象,值为 "Hello"。 对比 new String("Hello"),new 关键字会绕过字符串常量池,直接在堆中创建一个新对象。如果常量池中已存在 "Hello": → 仅在堆中创建新对象。 总共创建一个对象。如果常量池中不存在 "Hello": → 先在常量池中创建 "Hello" 对象,再在堆中创建新对象。 总共创建两个对象。
在动态代理中,反射的作用:
- 识别目标方法:通过反射获取目标方法的Method对象
- 动态调用:用Method.invoke()在代理对象内部调用目标方法,而不是编译时硬编码
- 实现通用代理逻辑:不用为每个方法写具体调用代码,可以适应不同的方法签名和类
反射本身不是“动态加载”的,但它是在运行时动态操作类和方法的机制。类的加载(如Class.forName())是“动态加载”,反射机制(如Method.invoke())是在类加载后,用于动态操作已加载的类和对象。反射的核心作用:允许你在程序运行时,动态地获取类的信息(如方法、字段、构造函数)并调用它们。
在动态代理中,是否使用反射对比:
| 比较 | 方案1(通用反射调用) | 方案2(方法个性化逻辑) |
|---|---|---|
| 复杂度 | 低,逻辑集中 | 高,需要判断和分支处理每个方法 |
| 灵活性 | 低,只能做通用逻辑 | 高,能对每个方法做特殊处理 |
| 维护成本 | 低,修改代理只需一次 | 高,新增或修改方法逻辑要修改多个地方 |
| 示例代码 | 简洁,重复利用 | 细致,分支多 |

浙公网安备 33010602011771号