[{"content":"什么是反射？ 正射： 编译期就确定要操作的类，代码写死\n1 2 User user = new User(); user.setName(\u0026#34;张三\u0026#34;); 特点：编译期类型检查，性能高，灵活性差\n反射： 运行期才动态获取类的信息、操作类的成员\n1 Class.forName(\u0026#34;com.example.User\u0026#34;).getMethod(\u0026#34;setName\u0026#34;).invoke(obj, \u0026#34;张三\u0026#34;); 特点：运行期动态解析，性能略低+灵活性极强\n反射的底层原理 Java 类加载机制是反射的基础：\n.java 源码编译为 .class 字节码文件 类加载器将 .class 加载到 JVM 内存，生成唯一的 Class 对象（包含类的所有元信息） 反射就是操作这个 Class 对象；反向获取和操作类的一切 反射相关的主要类 类名 所在包 核心作用 Class java.lang 反射的入口，代表一个类的元信息 Constructor java.lang.reflect 代表类的构造器，用于动态创建对象 Field java.lang.reflect 代表类的成员变量，用于动态获取 / 修改属性值 Method java.lang.reflect 代表类的成员方法，用于动态调用方法 AccessibleObject java.lang.reflect 上述 3 个类的父类，提供关闭访问检查的能力 反射的作用（为什么要用反射？） 反射是 Java 生态的基石，没有反射就没有现代 Java 框架，核心作用有 3 个：\n1. 实现框架的动态性和可扩展性 这是反射最核心的应用场景：\nSpring IOC：读取配置文件 / 注解中的类全限定名，通过反射动态创建 Bean 并管理 MyBatis：Mapper 接口的动态代理，通过反射生成实现类，动态调用 SQL JDBC 驱动加载：Class.forName(\u0026quot;com.mysql.cj.jdbc.Driver\u0026quot;) 就是通过反射加载驱动 2.编写通用工具类 避免为每个类写重复逻辑，实现代码复用：\n对象属性拷贝：Spring BeanUtils.copyProperties()、Apache BeanUtils，底层都是反射 JSON 序列化：Jackson、Gson，通过反射获取对象所有属性；序列化为 JSON 3. 突破封装限制（特殊场景） 可以操作类的 private 成员，处理特殊需求（如单元测试、框架内部逻辑）。\n反射的优缺点 优点 极强的灵活性：编译期不依赖具体类，运行期动态加载，是框架可扩展性的核心 代码复用性高：可以编写通用工具类，避免重复代码 突破封装：可以操作私有成员，处理特殊场景 缺点 性能损耗：反射是运行期动态解析，比直接调用慢 10~100 倍，频繁使用会严重影响性能 破坏封装性：操作私有成员违反了面向对象的封装原则，可能导致安全问题 编译期无法检查错误：类、方法、属性是否存在，只有运行期才会抛异常，增加调试难度 代码可读性差：反射代码比常规代码复杂，不利于后期维护 反射调用优化（3 个核心手段） 1.缓存反射对象（最有效、最常用） 不要每次都重新获取 Class、Constructor、Method、Field 对象，缓存起来复用，性能可以提升几十倍。 代码示例：优化前后对比\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 import java.lang.reflect.Method; public class ReflectOptimization { // 优化前：每次调用都重新获取Method对象 public static void noCache() throws Exception { for (int i = 0; i \u0026lt; 100000; i++) { User user = new User(); Method method = User.class.getMethod(\u0026#34;setName\u0026#34;, String.class); method.invoke(user, \u0026#34;张三\u0026#34;); } } // 优化后：缓存Method对象，复用 private static final Method SET_NAME_METHOD; static { try { SET_NAME_METHOD = User.class.getMethod(\u0026#34;setName\u0026#34;, String.class); } catch (NoSuchMethodException e) { throw new RuntimeException(e); } } public static void withCache() throws Exception { for (int i = 0; i \u0026lt; 100000; i++) { User user = new User(); SET_NAME_METHOD.invoke(user, \u0026#34;张三\u0026#34;); } } // 性能测试 public static void main(String[] args) throws Exception { long start1 = System.currentTimeMillis(); noCache(); System.out.println(\u0026#34;无缓存耗时：\u0026#34; + (System.currentTimeMillis() - start1) + \u0026#34;ms\u0026#34;); long start2 = System.currentTimeMillis(); withCache(); System.out.println(\u0026#34;有缓存耗时：\u0026#34; + (System.currentTimeMillis() - start2) + \u0026#34;ms\u0026#34;); } } 2.使用 MethodHandle（JDK 7+ 推荐） MethodHandle 是 JDK 7 引入的轻量级反射 API，性能比传统反射高很多，接近直接调用。 代码示例：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; public class MethodHandleDemo { public static void main(String[] args) throws Throwable { // 1. 获取MethodHandles.Lookup MethodHandles.Lookup lookup = MethodHandles.lookup(); // 2. 定义方法类型：返回值void，参数String MethodType methodType = MethodType.methodType(void.class, String.class); // 3. 查找方法 MethodHandle setNameHandle = lookup.findVirtual(User.class, \u0026#34;setName\u0026#34;, methodType); // 4. 调用方法 User user = new User(); setNameHandle.invoke(user, \u0026#34;李四\u0026#34;); System.out.println(user.getName()); // 输出：李四 } } 避免频繁调用 setAccessible(true) setAccessible(true) 本身也有性能损耗，只需要调用一次，后续复用即可（配合缓存使用）。\n关闭访问检查：setAccessible(true) 作用： AccessibleObject 是 Constructor、Field、Method 的父类，提供 setAccessible(boolean flag) 方法：\n默认 false：开启访问检查，无法访问 private 成员 设为 true：关闭 JVM 的访问权限检查，可以访问 private 成员（俗称「暴力反射」） 代码示例： 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import java.lang.reflect.Field; public class SetAccessibleDemo { public static void main(String[] args) throws Exception { User user = new User(); // 获取private属性name Field nameField = User.class.getDeclaredField(\u0026#34;name\u0026#34;); // ❌ 不关闭访问检查，直接操作会抛异常：IllegalAccessException // nameField.set(user, \u0026#34;张三\u0026#34;); // ✅ 关闭访问检查 nameField.setAccessible(true); nameField.set(user, \u0026#34;张三\u0026#34;); System.out.println(nameField.get(user)); // 输出：张三 } } 注意事项\n不是修改访问修饰符：setAccessible(true) 只是关闭了 JVM 的检查，private 依然是 private，代码层面的访问修饰符没有改变 安全管理器限制：如果 JVM 启动了安全管理器（SecurityManager），可能会禁止调用 setAccessible(true) 只需要调用一次：配合缓存使用，不要每次都调用 Class类 什么是 Class 类？ 当 JVM 加载一个 .class 字节码文件时，会在堆内存中自动生成唯一的一个 Class 对象。这个对象就像类的 \u0026ldquo;身份证\u0026rdquo;，包含了该类的所有结构信息，反射就是通过操作这个对象，反向获取和操作类的一切。\nClass 类的核心特点 没有公共构造方法\n不能用 new Class() 创建实例，只能通过 JVM 自动生成或 3 种固定方式获取 构造方法是私有的：private Class(ClassLoader loader) 单例性\nJVM 中，一个类永远只有一个 Class 实例 无论用哪种方式获取，得到的都是同一个对象 1 2 3 4 Class\u0026lt;?\u0026gt; c1 = Class.forName(\u0026#34;com.example.User\u0026#34;); Class\u0026lt;?\u0026gt; c2 = User.class; Class\u0026lt;?\u0026gt; c3 = new User().getClass(); System.out.println(c1 == c2 \u0026amp;\u0026amp; c2 == c3); // true 不可变性\nClass 对象一旦被 JVM 加载，其内容就不可改变 不能通过 Class 对象修改类的结构（只能读取和调用） 会触发类的加载和初始化\nClass.forName(\u0026quot;全类名\u0026quot;) 会触发类的静态代码块执行 类名.class 和 对象.getClass() 不会触发静态初始化 Class 类的常用方法及演示 演示对象\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package com.example; public class User { private String name; private int age; public String gender; public User() {} private User(String name, int age) { this.name = name; this.age = age; } public void sayHello() { System.out.println(\u0026#34;Hello, \u0026#34; + name); } private String getSecret() { return \u0026#34;I love Java\u0026#34;; } // getter/setter public String getName() { return name; } public void setName(String name) { this.name = name; } } 获取 Class 对象的方法 这是所有反射操作的第一步，3 种方式各有适用场景： 1.Class.forName(String className) 例：Class.forName(\u0026quot;com.example.User\u0026quot;)\n框架首选，编译期不依赖类，运行期动态加载，会触发静态初始化\n2.类名.class 例：User.class\n编译期确定，需要导入类包，不会触发静态初始化，多用于参数传递\n3.对象.getClass() 例：new User().getClass()\n已有对象时使用，能获取对象的实际运行类（适配多态）\n创建对象的方法 通过 Class 对象动态创建类的实例： 1.T newInstance()：通过无参构造创建对象\nJDK9 已废弃，推荐使用其他方法\n2.Constructor\u0026lt;T\u0026gt; getConstructor(Class\u0026lt;?\u0026gt;... parameterTypes)：获取 public 无参 / 有参构造器\n只能获取 public 的\n3.Constructor\u0026lt;T\u0026gt; getDeclaredConstructor(Class\u0026lt;?\u0026gt;... parameterTypes)：获取任意构造器（包括 private）\n配合 setAccessible(true) 使用\n代码示例：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import java.lang.reflect.Constructor; public class ClassCreateObjectDemo { public static void main(String[] args) throws Exception { Class\u0026lt;User\u0026gt; userClass = User.class; // 1. 推荐方式：通过public无参构造创建对象 Constructor\u0026lt;User\u0026gt; noArgConstructor = userClass.getConstructor(); User user1 = noArgConstructor.newInstance(); user1.setName(\u0026#34;张三\u0026#34;); System.out.println(\u0026#34;无参构造创建：\u0026#34; + user1.getName()); // 2. 通过private有参构造创建对象（暴力反射） Constructor\u0026lt;User\u0026gt; privateConstructor = userClass.getDeclaredConstructor(String.class, int.class); privateConstructor.setAccessible(true); // 关闭访问检查 User user2 = privateConstructor.newInstance(\u0026#34;李四\u0026#34;, 20); System.out.println(\u0026#34;私有构造创建：\u0026#34; + user2.getName()); } } 获取构造器的方法 获取类的所有构造器信息： 1.Constructor\u0026lt;?\u0026gt;[] getConstructors()：获取所有 public 构造器 2.Constructor\u0026lt;?\u0026gt;[] getDeclaredConstructors()：获取所有构造器（包括 private、protected、default）\n代码示例：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import java.lang.reflect.Constructor; public class ClassGetConstructorsDemo { public static void main(String[] args) { Class\u0026lt;User\u0026gt; userClass = User.class; System.out.println(\u0026#34;所有public构造器：\u0026#34;); Constructor\u0026lt;?\u0026gt;[] publicConstructors = userClass.getConstructors(); for (Constructor\u0026lt;?\u0026gt; constructor : publicConstructors) { System.out.println(\u0026#34;- \u0026#34; + constructor); } System.out.println(\u0026#34;\\n所有构造器（包括私有）：\u0026#34;); Constructor\u0026lt;?\u0026gt;[] allConstructors = userClass.getDeclaredConstructors(); for (Constructor\u0026lt;?\u0026gt; constructor : allConstructors) { System.out.println(\u0026#34;- \u0026#34; + constructor); } } } 获取成员变量的方法 获取类的所有属性信息： 1.Field[] getFields()：获取所有 public 属性 2.Field[] getDeclaredFields()：获取所有属性（包括 private） 3.Field getField(String name)：获取指定名称的 public 属性 4.Field getDeclaredField(String name)：获取指定名称的任意属性（包括 private）\n代码示例：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 import java.lang.reflect.Field; public class ClassGetFieldsDemo { public static void main(String[] args) throws Exception { Class\u0026lt;User\u0026gt; userClass = User.class; User user = userClass.getConstructor().newInstance(); // 获取并操作private属性 Field nameField = userClass.getDeclaredField(\u0026#34;name\u0026#34;); nameField.setAccessible(true); nameField.set(user, \u0026#34;王五\u0026#34;); System.out.println(\u0026#34;name属性值：\u0026#34; + nameField.get(user)); } } 获取成员方法的方法 获取类的所有方法信息： 1.Method[] getMethods()：获取所有 public 方法（包括父类继承的） 2.Method[] getDeclaredMethods()：获取本类所有方法（包括 private，不包括继承的 3.Method getMethod(String name, Class\u0026lt;?\u0026gt;... parameterTypes)：获取指定名称和参数的 public 方法 4.Method getDeclaredMethod(String name, Class\u0026lt;?\u0026gt;... parameterTypes)：获取指定名称和参数的任意方法（包括 private）\n代码示例：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import java.lang.reflect.Method; public class ClassGetMethodsDemo { public static void main(String[] args) throws Exception { Class\u0026lt;User\u0026gt; userClass = User.class; User user = userClass.getDeclaredConstructor(String.class, int.class).newInstance(\u0026#34;赵六\u0026#34;, 22); user.setAccessible(true); // 调用public方法 Method sayHelloMethod = userClass.getMethod(\u0026#34;sayHello\u0026#34;); sayHelloMethod.invoke(user); // 输出：Hello, 赵六 // 调用private方法 Method getSecretMethod = userClass.getDeclaredMethod(\u0026#34;getSecret\u0026#34;); getSecretMethod.setAccessible(true); String secret = (String) getSecretMethod.invoke(user); System.out.println(\u0026#34;私有方法返回值：\u0026#34; + secret); } } 获取类基本信息的方法 获取类的元数据信息： 1.String getName()：获取全类名（包名 + 类名） 2.String getSimpleName()：获取简单类名 3.Package getPackage()：获取包信息 4.Class\u0026lt;? super T\u0026gt; getSuperclass()：获取父类 Class 对象 5.Class\u0026lt;?\u0026gt;[] getInterfaces()：获取实现的所有接口 6.boolean isInterface()：是否是接口 7.boolean isArray()：是否是数组 8.boolean isPrimitive()：是否是基本数据类型\n代码示例：\n1 2 3 4 5 6 7 8 9 10 11 12 public class ClassInfoDemo { public static void main(String[] args) { Class\u0026lt;User\u0026gt; userClass = User.class; System.out.println(\u0026#34;全类名：\u0026#34; + userClass.getName()); // com.example.User System.out.println(\u0026#34;简单类名：\u0026#34; + userClass.getSimpleName()); // User System.out.println(\u0026#34;包名：\u0026#34; + userClass.getPackage().getName()); // com.example System.out.println(\u0026#34;父类：\u0026#34; + userClass.getSuperclass().getName()); // java.lang.Object System.out.println(\u0026#34;是否是接口：\u0026#34; + userClass.isInterface()); // false System.out.println(\u0026#34;是否是数组：\u0026#34; + userClass.isArray()); // false } } 注意事项： Class.forName() 会触发静态初始化\n如果类中有静态代码块，Class.forName() 会执行它 类名.class 和 对象.getClass() 不会执行静态代码块 基本数据类型的 Class 对象\n基本数据类型也有对应的 Class 对象：int.class、boolean.class 它们的包装类有一个 TYPE 常量，指向对应的基本类型 Class 对象：Integer.TYPE == int.class 数组的 Class 对象\n数组的 Class 对象是 元素类型[].class：String[].class、int[].class 所有同类型同维度的数组共享同一个 Class 对象 newInstance() 方法已废弃\nJDK9 之后，Class.newInstance() 被废弃，因为它会传播未检查的异常 推荐使用 Constructor.newInstance() 方法创建对象 1 2 3 4 5 6 7 8 9 import java.lang.reflect.Constructor; Class\u0026lt;User\u0026gt; userClass = User.class; // ✅ 调用者是 Class 对象 → 执行 Class.newInstance() User user1 = userClass.newInstance(); // ✅ 调用者是 Constructor 对象 → 执行 Constructor.newInstance() Constructor\u0026lt;User\u0026gt; constructor = userClass.getConstructor(); User user2 = constructor.newInstance(); 获取 Class 类对象 哪些类型有 Class 对象？ Java 中几乎所有类型都有对应的 Class 对象，JVM 会为每一种类型在内存中生成唯一的 Class 实例。具体包括以下 8 大类：\n类型分类 示例 Class 对象写法 普通类 User、String、ArrayList User.class 接口 List、Runnable、Serializable List.class 数组 int[]、String[]、User[][] int[].class、String[][].class 基本数据类型 int、boolean、char、double int.class、boolean.class 枚举 enum Color {RED, GREEN} Color.class 注解 @Override、@Service Override.class 泛型参数 T、E 无法直接获取，通过反射间接获取 void 无返回值 void.class 关键验证：Class 对象的单例性\n同一个类型在同一个 JVM 中永远只有一个 Class 实例，无论用哪种方式获取，得到的都是同一个对象\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class ClassSingletonDemo { public static void main(String[] args) { // 验证普通类 Class\u0026lt;?\u0026gt; c1 = User.class; Class\u0026lt;?\u0026gt; c2 = new User().getClass(); System.out.println(c1 == c2); // true // 验证数组 Class\u0026lt;?\u0026gt; arr1 = int[].class; Class\u0026lt;?\u0026gt; arr2 = new int[10].getClass(); System.out.println(arr1 == arr2); // true // 验证基本数据类型 System.out.println(int.class == int.class); // true } } class User {} 获取 Class 对象的 3 种核心方法 方法 1：Class.forName(String 全类名) 最灵活、框架首选的方式，编译期不需要依赖类，运行期动态加载。\n1 Class\u0026lt;?\u0026gt; clazz = Class.forName(\u0026#34;包名.类名\u0026#34;); 核心特点 会触发类的完整初始化：执行静态代码块、静态变量赋值 编译期不依赖类：只要运行时类存在即可，是框架实现可扩展性的核心 抛出检查异常：ClassNotFoundException（类不存在时） 适用场景 框架底层动态加载类（Spring IOC、MyBatis、JDBC） 配置文件中指定类名，运行时动态创建对象 插件化开发 代码示例： 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class ForNameDemo { public static void main(String[] args) { try { // 1. 加载普通类 Class\u0026lt;?\u0026gt; userClass = Class.forName(\u0026#34;com.example.User\u0026#34;); System.out.println(\u0026#34;加载User类成功：\u0026#34; + userClass.getName()); // 2. JDBC 加载驱动（经典应用） Class.forName(\u0026#34;com.mysql.cj.jdbc.Driver\u0026#34;); System.out.println(\u0026#34;MySQL驱动加载成功\u0026#34;); } catch (ClassNotFoundException e) { System.err.println(\u0026#34;类不存在：\u0026#34; + e.getMessage()); } } } // 验证静态代码块执行 class User { static { System.out.println(\u0026#34;User类的静态代码块执行了\u0026#34;); } } 方法 2：类名.class 最安全、性能最高的方式，编译期就确定了 Class 对象。 语法：\n1 2 3 Class\u0026lt;User\u0026gt; userClass = User.class; Class\u0026lt;Integer\u0026gt; intClass = int.class; Class\u0026lt;List\u0026gt; listClass = List.class; 核心特点 不会触发类的初始化：不执行静态代码块，性能最好 编译期类型检查：如果类不存在，编译直接报错，更安全 支持泛型：可以得到带泛型的 Class 对象，避免类型转换 适用场景 方法参数传递（比如获取方法的参数类型、返回值类型） 已知类名的情况下，优先使用这种方式 基本数据类型只能用这种方式获取 Class 对象 代码示例： 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class ClassLiteralDemo { public static void main(String[] args) { // 1. 普通类 Class\u0026lt;User\u0026gt; userClass = User.class; System.out.println(\u0026#34;User类：\u0026#34; + userClass.getSimpleName()); // 2. 基本数据类型（只能用这种方式） Class\u0026lt;Integer\u0026gt; intClass = int.class; System.out.println(\u0026#34;int类型：\u0026#34; + intClass.getName()); // 3. 接口 Class\u0026lt;Runnable\u0026gt; runnableClass = Runnable.class; System.out.println(\u0026#34;Runnable接口：\u0026#34; + runnableClass.getName()); // 4. 数组 Class\u0026lt;String[]\u0026gt; stringArrayClass = String[].class; System.out.println(\u0026#34;String数组：\u0026#34; + stringArrayClass.getName()); // 5. void Class\u0026lt;Void\u0026gt; voidClass = void.class; System.out.println(\u0026#34;void类型：\u0026#34; + voidClass.getName()); } } 方法 3：对象.getClass() 已有对象时使用，可以获取对象的实际运行时类型（适配多态）。 语法：\n1 2 Object obj = new User(); Class\u0026lt;?\u0026gt; clazz = obj.getClass(); 核心特点 获取的是对象的实际运行类，不是声明的类型（多态场景下非常重要） 不会触发类的初始化（因为对象已经创建了） 只能用于引用类型，基本数据类型不能使用 适用场景 多态场景下，获取对象的真实类型 已有对象实例，需要获取其 Class 对象 代码示例： 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class GetClassDemo { public static void main(String[] args) { // 声明类型是 Animal，实际类型是 Dog Animal animal = new Dog(); // getClass() 返回实际运行时的类 Dog.class Class\u0026lt;?\u0026gt; clazz = animal.getClass(); System.out.println(\u0026#34;对象的实际类型：\u0026#34; + clazz.getName()); // com.example.Dog // 对比：类名.class 返回声明的类型 Animal.class System.out.println(\u0026#34;声明的类型：\u0026#34; + Animal.class.getName()); // com.example.Animal } } class Animal {} class Dog extends Animal {} 3 种方法对比与选型 对比维度 Class.forName() 类名.class 对象.getClass() 触发初始化 ✅ 是 ❌ 否 ❌ 否 编译期检查 ❌ 否（运行时才检查） ✅ 是 ✅ 是 性能 最低 最高 高 灵活性 最高 最低 中等 适用场景 框架动态加载、插件化 已知类名、参数传递 已有对象、多态场景 特殊场景下的 Class 对象获取 1. 基本数据类型 vs 包装类的 Class 对象 基本数据类型和其包装类的 Class 对象是不同的\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class PrimitiveVsWrapperDemo { public static void main(String[] args) { // 基本数据类型 Class\u0026lt;Integer\u0026gt; intClass = int.class; // 包装类 Class\u0026lt;Integer\u0026gt; integerClass = Integer.class; System.out.println(intClass == integerClass); // false // 包装类的 TYPE 常量指向对应的基本类型 Class 对象 System.out.println(intClass == Integer.TYPE); // true System.out.println(boolean.class == Boolean.TYPE); // true } } 2. 数组的 Class 对象\n所有同类型、同维度的数组共享同一个 Class 对象 不同维度的数组，Class 对象不同 1 2 3 4 5 6 7 8 9 10 public class ArrayClassDemo { public static void main(String[] args) { int[] arr1 = new int[10]; int[] arr2 = new int[100]; int[][] arr3 = new int[5][5]; System.out.println(arr1.getClass() == arr2.getClass()); // true（同类型同维度） System.out.println(arr1.getClass() == arr3.getClass()); // false（不同维度） } } 3.匿名内部类的 Class 对象 匿名内部类也有对应的 Class 对象，类名格式为 外部类名$数字：\n1 2 3 4 5 6 7 8 9 10 11 public class AnonymousClassDemo { public static void main(String[] args) { Runnable runnable = new Runnable() { @Override public void run() {} }; System.out.println(runnable.getClass().getName()); // 输出：com.example.AnonymousClassDemo$1 } } 常见坑与注意事项 Class.forName() 必须写全类名（包名 + 类名），否则会报 ClassNotFoundException 基本数据类型只能用 类名.class 获取 Class 对象，不能用 Class.forName() 或 对象.getClass() getClass() 返回的是实际运行时类型，不是声明的类型，多态场景下要特别注意 数组的 Class 对象与元素类型和维度都有关，不同维度的数组 Class 对象不同 Class 对象是单例的，可以用 == 比较两个 Class 对象是否相等 类加载 类加载的基本介绍 什么是类加载？ JVM 在第一次使用某个类时，会将该类的.class字节码文件从磁盘（或网络、内存等）读取到内存中，并对其进行验证、准备、解析、初始化，最终生成一个可以被程序使用的Class对象。这个完整的过程，就是类加载。\n核心特点：\n按需加载：JVM 不会一次性加载所有类，只有在第一次使用时才会加载 一次加载，终身复用：一个类在 JVM 的生命周期中只会被加载一次，生成的Class对象会被缓存起来 可扩展：通过自定义类加载器，可以实现从任意来源加载类（如网络、加密文件、动态生成的字节码） 类加载与反射的关系 反射的本质是操作 JVM 加载后生成的Class对象。如果一个类没有被 JVM 加载，就无法获取它的Class对象，也就无法进行任何反射操作。\n类加载的时机 JVM 只有在主动引用一个类时，才会触发类的初始化（类加载的最后一个阶段）。如果是被动引用，则不会触发类的初始化。\n1. 主动引用（一定会触发类加载） 以下 6 种情况属于主动引用，JVM 会立即加载并初始化该类：\n创建类的实例：new User() 调用类的静态成员：访问或修改静态变量、调用静态方法 使用反射：Class.forName(\u0026quot;com.example.User\u0026quot;) 初始化子类：初始化子类时，会先初始化其父类 启动类：执行main()方法的类 JDK 8+ 接口默认方法：如果一个接口定义了默认方法，那么实现该接口的类初始化时，会先初始化该接口 2. 被动引用（不会触发类加载） 以下 3 种情况属于被动引用，JVM 不会触发类的初始化：\n通过子类引用父类的静态变量：只会初始化父类，不会初始化子类 定义类的数组：User[] users = new User[10]; 只会初始化数组本身，不会初始化User类 引用类的常量：常量在编译阶段会被存入调用类的常量池，本质上没有直接引用定义常量的类 代码示例：主动引用 vs 被动引用：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 public class ClassLoadTimingDemo { public static void main(String[] args) { // 被动引用1：通过子类引用父类的静态变量 // 只会输出\u0026#34;父类初始化\u0026#34;，不会输出\u0026#34;子类初始化\u0026#34; System.out.println(Child.parentStaticVar); // 被动引用2：定义类的数组 // 不会输出任何初始化信息 User[] users = new User[10]; // 被动引用3：引用类的常量 // 不会输出\u0026#34;常量类初始化\u0026#34; System.out.println(ConstantClass.CONSTANT); // 主动引用：创建对象 // 会输出\u0026#34;User类初始化\u0026#34; User user = new User(); } } class Parent { public static int parentStaticVar = 100; static { System.out.println(\u0026#34;父类初始化\u0026#34;); } } class Child extends Parent { static { System.out.println(\u0026#34;子类初始化\u0026#34;); } } class User { static { System.out.println(\u0026#34;User类初始化\u0026#34;); } } class ConstantClass { public static final String CONSTANT = \u0026#34;hello\u0026#34;; static { System.out.println(\u0026#34;常量类初始化\u0026#34;); } } 类加载的完整过程 类加载分为5 个连续的阶段，其中前 4 个阶段（加载、验证、准备、解析）称为连接阶段，最后一个阶段是初始化阶段。\n1 2 3 类加载流程： 加载 → 验证 → 准备 → 解析 → 初始化 └────────── 连接阶段 ─┘ 阶段 1：加载（Loading） 核心任务：找到字节码文件，读取到内存，生成Class对象。\n具体完成 3 件事： 获取字节码：通过类的全限定名（包名 + 类名），找到对应的.class字节码文件 字节码来源：本地磁盘、网络（如 Tomcat 加载 war 包）、动态生成（如 ASM、CGLIB）、加密文件 转化数据结构：将字节码的静态二进制结构，转化为方法区的运行时数据结构 生成 Class 对象：在堆内存中生成一个对应的java.lang.Class对象，作为方法区中类数据的访问入口 关键说明： 加载阶段由 类加载器（ClassLoader） 完成 加载阶段和连接阶段是交叉进行的，加载阶段还没结束，连接阶段可能已经开始 数组类的加载比较特殊：数组类本身由 JVM 直接创建，其元素类型由类加载器加载 阶段 2：验证（Verification） 核心任务：确保字节码文件的正确性，防止恶意代码攻击 JVM。 这是 JVM 的安全屏障，确保加载的字节码符合 JVM 规范，不会导致 JVM 崩溃。\n验证分为 4 个步骤： 文件格式验证：验证字节码文件的格式是否符合规范\n检查魔数（0xCAFEBABE）、版本号、常量池格式等 元数据验证：验证类的元数据信息是否符合 Java 语法规范\n检查类是否有父类、是否继承了不允许继承的类（如final类）、方法和字段是否符合语法 字节码验证：最复杂的一步，验证方法的字节码逻辑是否合法\n检查操作数栈的类型是否正确、跳转指令是否指向正确的位置、类型转换是否合法 符号引用验证：验证常量池中的符号引用是否存在且合法\n检查符号引用指向的类、方法、字段是否存在，访问权限是否合法 阶段 3：准备（Preparation） 核心任务：为类的静态变量分配内存，并设置默认初始值。\n关键细节： 只处理静态变量：实例变量的内存分配是在对象创建时（堆中）进行的 设置默认初始值：不是代码中写的初始值，而是 JVM 规定的默认值 final修饰的静态常量：在准备阶段就会被赋值为代码中写的初始值（因为常量在编译期就确定了） 类型 默认初始值 int 0 long 0L float 0.0f double 0.0d boolean false char \u0026lsquo;\\u0000\u0026rsquo; 引用类型 null 代码示例：准备阶段 vs 初始化阶段的赋值 1 2 3 4 5 6 7 8 9 10 11 12 13 public class PreparePhaseDemo { // 准备阶段：分配内存，设置默认值0 // 初始化阶段：赋值为100 public static int a = 100; // 准备阶段：直接赋值为200（final修饰的静态常量） public static final int b = 200; public static void main(String[] args) { System.out.println(a); // 100 System.out.println(b); // 200 } } 阶段 4：解析（Resolution） 核心任务：将常量池中的符号引用，转化为直接引用。\n核心概念： 符号引用：用一组字符串来描述所引用的目标，比如com.example.User#sayHello() 编译期无法确定目标的内存地址，只能用符号引用表示 直接引用：指向目标内存地址的指针、偏移量或句柄 解析后，JVM 可以直接通过直接引用找到目标 解析的主要内容： 类或接口的解析 字段的解析 类方法的解析 接口方法的解析 关键说明： 解析阶段可以在初始化阶段之后再执行（称为动态解析），这是 Java 实现动态绑定（多态）的基础 解析阶段只会执行一次，解析后的直接引用会被缓存起来 阶段 5：初始化（Initialization） 核心任务：执行类的静态变量赋值语句和静态代码块，完成类的初始化。 这是类加载的最后一个阶段，也是我们最熟悉的阶段。\n核心执行逻辑： JVM 会自动生成一个类构造器方法\u0026lt;clinit\u0026gt;()，初始化阶段就是执行这个方法的过程。 \u0026lt;clinit\u0026gt;()方法由编译器自动生成，包含：\n所有静态变量的赋值语句 所有静态代码块（static{}）中的代码 初始化的执行顺序： 父类先初始化，子类后初始化：执行子类的\u0026lt;clinit\u0026gt;()方法前，会先执行父类的\u0026lt;clinit\u0026gt;()方法 静态变量和静态代码块按顺序执行：在类中定义的顺序，就是执行的顺序 \u0026lt;clinit\u0026gt;()方法只会执行一次：JVM 会保证\u0026lt;clinit\u0026gt;()方法在多线程环境下被正确加锁和同步，确保一个类只会被初始化一次 代码示例：初始化顺序 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public class InitializationOrderDemo { public static void main(String[] args) { new Son(); } } class Father { static { System.out.println(\u0026#34;父类静态代码块\u0026#34;); } public Father() { System.out.println(\u0026#34;父类构造方法\u0026#34;); } } class Son extends Father { static { System.out.println(\u0026#34;子类静态代码块\u0026#34;); } public Son() { System.out.println(\u0026#34;子类构造方法\u0026#34;); } } 类加载各阶段任务总结 阶段 核心任务 关键细节 加载 读取字节码，生成 Class 对象 由类加载器完成，字节码来源多样 验证 确保字节码的正确性和安全性 4 个验证步骤，JVM 的安全屏障 准备 为静态变量分配内存，设置默认初始值 final 静态常量直接赋值，实例变量不处理 解析 符号引用转化为直接引用 支持动态解析，是多态的基础 初始化 执行静态变量赋值和静态代码块 执行\u0026lt;clinit\u0026gt;()方法，父类先初始化 常见问题与注意事项 类加载的顺序：静态代码块 → 构造代码块 → 构造方法 静态变量的赋值：准备阶段赋默认值，初始化阶段赋代码中的值 被动引用不会触发初始化：这是很多面试题的考点 \u0026lt;clinit\u0026gt;() vs \u0026lt;init\u0026gt;()： \u0026lt;clinit\u0026gt;()：类构造器，初始化类的静态成员，只执行一次 \u0026lt;init\u0026gt;()：对象构造器，初始化对象的实例成员，每次创建对象都会执行 类加载器的双亲委派模型：JVM 默认采用双亲委派模型加载类，确保核心类的安全性 通过反射获取类的结构信息 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 // 父类 class Base { public String basePublicField; private int basePrivateField; public void basePublicMethod() {} private void basePrivateMethod() {} } // 子类（演示用） @Deprecated public class Student extends Base { // 不同修饰符的属性 public String name; protected int age; private String address; public static final String SCHOOL = \u0026#34;清华大学\u0026#34;; // 不同修饰符的构造器 public Student() {} public Student(String name) { this.name = name; } private Student(String name, int age) { this.name = name; this.age = age; } // 不同修饰符的方法 public void study() {} protected void eat() {} private void sleep() {} public static void run() {} } java.lang.Class 类 ![[Pasted image 20260407211101.png]] 所有获取类结构信息的操作，都从 Class 对象开始。 代码示例：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; public class ReflectionUtilsDemo { public static void main(String[] args) throws Exception { Class\u0026lt;Student\u0026gt; studentClass = Student.class; // 1. getName: 获取全类名（包名+类名） System.out.println(\u0026#34;1. 全类名：\u0026#34; + studentClass.getName()); // 输出：com.hspedu.reflection.Student // 2. getSimpleName: 获取简单类名 System.out.println(\u0026#34;\\n2. 简单类名：\u0026#34; + studentClass.getSimpleName()); // 输出：Student // 3. getFields: 获取所有public修饰的属性，包含本类以及父类的 // 不能访问私有成员 System.out.println(\u0026#34;\\n3. 所有public属性（含父类）：\u0026#34;); Field[] publicFields = studentClass.getFields(); for (Field field : publicFields) { System.out.println(\u0026#34;- \u0026#34; + field.getName()); } // 输出：name, SCHOOL, basePublicField // 4. getDeclaredFields: 获取本类中所有属性（不包含父类） // 配合 setAccessible(true)能访问私有成员 System.out.println(\u0026#34;\\n4. 本类所有属性（不含父类）：\u0026#34;); Field[] allFields = studentClass.getDeclaredFields(); for (Field field : allFields) { System.out.println(\u0026#34;- \u0026#34; + field.getName()); } // 输出：name, age, address, SCHOOL // 5. getMethods: 获取所有public修饰的方法，包含本类以及父类的 System.out.println(\u0026#34;\\n5. 所有public方法（含父类）：\u0026#34;); Method[] publicMethods = studentClass.getMethods(); for (Method method : publicMethods) { System.out.println(\u0026#34;- \u0026#34; + method.getName()); } // 输出：study, run, basePublicMethod, wait, equals, toString, hashCode...（Object类的方法） // 6. getDeclaredMethods: 获取本类中所有方法（不包含父类） System.out.println(\u0026#34;\\n6. 本类所有方法（不含父类）：\u0026#34;); Method[] allMethods = studentClass.getDeclaredMethods(); for (Method method : allMethods) { System.out.println(\u0026#34;- \u0026#34; + method.getName()); } // 输出：study, eat, sleep, run // 7. getConstructors: 获取本类所有public修饰的构造器 System.out.println(\u0026#34;\\n7. 所有public构造器：\u0026#34;); Constructor\u0026lt;?\u0026gt;[] publicConstructors = studentClass.getConstructors(); for (Constructor\u0026lt;?\u0026gt; constructor : publicConstructors) { System.out.println(\u0026#34;- \u0026#34; + constructor.getName()); } // 输出：com.hspedu.reflection.Student（无参）、com.hspedu.reflection.Student（一个参数） // 8. getDeclaredConstructors: 获取本类中所有构造器 System.out.println(\u0026#34;\\n8. 本类所有构造器：\u0026#34;); Constructor\u0026lt;?\u0026gt;[] allConstructors = studentClass.getDeclaredConstructors(); for (Constructor\u0026lt;?\u0026gt; constructor : allConstructors) { System.out.println(\u0026#34;- \u0026#34; + constructor.getName()); } // 输出：无参、一个参数、两个参数（私有） // 9. getPackage: 以Package形式返回包信息 System.out.println(\u0026#34;\\n9. 包信息：\u0026#34; + studentClass.getPackage().getName()); // 输出：com.hspedu.reflection // 10. getSuperClass: 以Class形式返回父类信息 System.out.println(\u0026#34;\\n10. 父类信息：\u0026#34; + studentClass.getSuperclass().getName()); // 输出：com.hspedu.reflection.Base // 11. getInterfaces: 以Class[]形式返回接口信息 System.out.println(\u0026#34;\\n11. 实现的接口：\u0026#34;); Class\u0026lt;?\u0026gt;[] interfaces = studentClass.getInterfaces(); for (Class\u0026lt;?\u0026gt; anInterface : interfaces) { System.out.println(\u0026#34;- \u0026#34; + anInterface.getName()); } // 输出：无（Student类没有实现接口） // 12. getAnnotations: 以Annotation[]形式返回注解信息 System.out.println(\u0026#34;\\n12. 类上的注解：\u0026#34;); Annotation[] annotations = studentClass.getAnnotations(); for (Annotation annotation : annotations) { System.out.println(\u0026#34;- \u0026#34; + annotation.annotationType().getName()); } // 输出：java.lang.Deprecated } } java.lang.reflect.Field 类 用于获取和操作类的属性信息。 ![[Pasted image 20260407211739.png]] 代码示例：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import java.lang.reflect.Field; import java.lang.reflect.Modifier; public class FieldDemo { public static void main(String[] args) { Class\u0026lt;Student\u0026gt; studentClass = Student.class; Field[] allFields = studentClass.getDeclaredFields(); for (Field field : allFields) { System.out.println(\u0026#34;属性名：\u0026#34; + field.getName()); // 1. getModifiers: 以int形式返回修饰符 int modifiers = field.getModifiers(); // 将int值转换为对应的修饰符字符串 String modifierStr = Modifier.toString(modifiers); System.out.println(\u0026#34;修饰符：\u0026#34; + modifierStr + \u0026#34;（int值：\u0026#34; + modifiers + \u0026#34;）\u0026#34;); // 2. getType: 以Class形式返回属性类型 Class\u0026lt;?\u0026gt; type = field.getType(); System.out.println(\u0026#34;属性类型：\u0026#34; + type.getName()); System.out.println(\u0026#34;------------------------\u0026#34;); } } } 多个修饰符是相加的关系：public(1) + static(8) + final(16) = 25\n修饰符 int 值 默认（包访问权限） 0 public 1 private 2 protected 4 static 8 final 16 java.lang.reflect.Method 类 用于获取和调用类的方法信息。 ![[Pasted image 20260407211751.png]] 代码示例：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 import java.lang.reflect.Method; import java.lang.reflect.Modifier; public class MethodDemo { public static void main(String[] args) { Class\u0026lt;Student\u0026gt; studentClass = Student.class; Method[] allMethods = studentClass.getDeclaredMethods(); for (Method method : allMethods) { System.out.println(\u0026#34;方法名：\u0026#34; + method.getName()); // 1. getModifiers: 以int形式返回修饰符 int modifiers = method.getModifiers(); String modifierStr = Modifier.toString(modifiers); System.out.println(\u0026#34;修饰符：\u0026#34; + modifierStr); // 2. getReturnType: 以Class形式获取返回类型 Class\u0026lt;?\u0026gt; returnType = method.getReturnType(); System.out.println(\u0026#34;返回类型：\u0026#34; + returnType.getName()); // 3. getParameterTypes: 以Class[]返回参数类型数组 Class\u0026lt;?\u0026gt;[] parameterTypes = method.getParameterTypes(); System.out.print(\u0026#34;参数类型：\u0026#34;); for (Class\u0026lt;?\u0026gt; parameterType : parameterTypes) { System.out.print(parameterType.getName() + \u0026#34; \u0026#34;); } System.out.println(\u0026#34;\\n------------------------\u0026#34;); } } } java.lang.reflect.Constructor 类 用于获取和调用类的构造器信息。 ![[Pasted image 20260407211802.png]] 代码示例：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 import java.lang.reflect.Constructor; import java.lang.reflect.Modifier; public class ConstructorDemo { public static void main(String[] args) { Class\u0026lt;Student\u0026gt; studentClass = Student.class; Constructor\u0026lt;?\u0026gt;[] allConstructors = studentClass.getDeclaredConstructors(); for (Constructor\u0026lt;?\u0026gt; constructor : allConstructors) { System.out.println(\u0026#34;构造器名：\u0026#34; + constructor.getName()); // 1. getModifiers: 以int形式返回修饰符 int modifiers = constructor.getModifiers(); String modifierStr = Modifier.toString(modifiers); System.out.println(\u0026#34;修饰符：\u0026#34; + modifierStr); // 2. getParameterTypes: 以Class[]返回参数类型数组 Class\u0026lt;?\u0026gt;[] parameterTypes = constructor.getParameterTypes(); System.out.print(\u0026#34;参数类型：\u0026#34;); for (Class\u0026lt;?\u0026gt; parameterType : parameterTypes) { System.out.print(parameterType.getName() + \u0026#34; \u0026#34;); } System.out.println(\u0026#34;\\n------------------------\u0026#34;); } } } ","date":"2026-04-11T00:00:00Z","permalink":"/p/java-note-21/","title":"Java 笔记 21：反射"},{"content":"这里开始写文章正文（支持 Markdown 格式） 一、什么是 Hugo Stack 主题 Stack 是一款简约、美观、高性能的 Hugo 主题，专注于内容展示，支持响应式布局。 这是文章的正文内容\u0026hellip;\n","date":"2026-04-10T12:52:04+08:00","permalink":"/p/%E6%88%91%E7%9A%84%E7%AC%AC%E4%B8%80%E7%AF%87%E6%96%87%E7%AB%A0/","title":"我的第一篇文章"}]