目录前言1. 字节码与类加载机制字节码类加载机制2. 获取 Class 对象的三种方式方式一Class.forName(全类名)方式二类名.class方式三对象.getClass()3. 获取 Class 信息获取构造方法获取字段获取方法总结前言通过本篇文章你将了解到 Java 反射的基本概念、为什么需要反射以及如何获取 Class 对象并进行基础操作。1. 字节码与类加载机制在学习反射之前我们先来简单了解一下字节码文件与 JVM 中的类加载机制。字节码我们编写的 Java 源代码保存在.java文件中。当我们在 IDE 中编译或运行代码时编译器会将这些源文件编译成 JVM 可执行的字节码文件其后缀为.class。类加载机制从编写代码到程序运行主要经历两个时期编译期和运行期。编译期程序员编写的.java代码被编译成.class字节码文件的阶段。运行期JVM 找到并加载.class文件将其数据读入内存中并在堆区生成一个对应的Class对象。这个阶段被称为类加载。这个Class对象存储了该类的结构信息如类名、父类、实现的接口、构造方法、属性和普通方法等。反射的核心就是通过获取这个Class对象来读取和操作该类的信息。2. 获取 Class 对象的三种方式理解了反射需要依赖Class对象后我们来看一下在 Java 中获取Class对象的三种常用方式方式一Class.forName(“全类名”)全类名的意思是package名这个类的名字假设有如下类packagecom.example;publicclassUser{// 省略属性与方法...}那么该类的全类名为com.example.User。获取其Class对象的完整写法Class?clazzClass.forName(com.example.User);关于Class?其含义是某种具体的类型的class对象。?是通配符表示某种未知的具体类型。通过名字去查找编译器无法确定该类具体是什么类型所以使用Class?。可以通过强制类型转换实现ClassUserclazz(ClassUser)Class.forName(com.example.User);由于是通过字符串名称查找找不到的话会抛出已检查异常ClassNotFoundException。方式二类名.class如果我们已经在写代码的时候明确知道要操作哪个类。ClassUserclazzUser.class;在编译期进行类型检查如果类名写错编译器会直接报错不需要处理ClassNotFoundException异常。方式三对象.getClass()已经拥有了某个类的对象实例时通过该实例获取其对应的Class对象UserbeannewUser();Class?extendsUserclazzbean.getClass();如果对象实例为null会抛出空指针异常NullPointerException。3. 获取 Class 信息获取到Class对象后可以获取类的构造方法、字段以及普通方法。获取构造方法获取指定的被public修饰的构造方法。clazz.getConstructor(Class?... parameterTypes)获取指定的任意修饰符包括private的构造方法。clazz.getDeclaredConstructor(Class?... parameterTypes)假设User类publicclassUser{privateStringname;privateIntegerage;// 构造方法一无参构造publicUser(){}// 构造方法二单参构造StringpublicUser(Stringname){this.namename;}// 构造方法三多参构造publicUser(Stringname,Integerage){this.namename;this.ageage;}// 构造方法四私有单参构造intprivateUser(intage){this.ageage;}}获取对应的构造器ClassUserclazzUser.class;// 获取无参构造方法ConstructorUserc1clazz.getDeclaredConstructor();// 获取单参构造方法StringConstructorUserc2clazz.getDeclaredConstructor(String.class);// 获取多参构造方法String, IntegerConstructorUserc3clazz.getDeclaredConstructor(String.class,Integer.class);// 获取私有构造方法intConstructorUserc4clazz.getDeclaredConstructor(int.class);利用获取到的构造方法创建对象c4.setAccessible(true);Userbeanc4.newInstance(18);private构造方法、字段或方法在操作前必须调用setAccessible(true)。默认情况下访问权限检查为false强行访问私有成员会抛出IllegalAccessException。获取字段获取指定的单个属性无论何种修饰符clazz.getDeclaredField(String name)获取类中声明的所有属性。clazz.getDeclaredFields()// 获取名为 name 的私有字段Fieldfieldclazz.getDeclaredField(name);// 解除私有访问限制field.setAccessible(true);// 为指定对象的该属性赋值field.set(bean,张三);// 获取指定对象的该属性值Stringname(String)field.get(bean);关于field.set(bean, 张三);写bean的原因是写明给哪一个实例化的对象的字段赋值获取方法获取指定的成员方法。需要传入方法名 参数类型的 Classclazz.getDeclaredMethod(String name, Class?... parameterTypes)调用该方法。传入要执行的对象实例和具体的参数值。method.invoke(Object obj, Object... args)// 获取 setName(String) 方法Methodmethodclazz.getDeclaredMethod(setName,String.class);// 执行方法method.invoke(bean,李四);// 如果是静态方法调用时对象实例传 null 即可// staticMethod.invoke(null, args);总结反射的核心是 Class 对象它是.class字节码文件被 JVM 加载到内存后生成的类型元数据表示。获取 Class 对象的三种方式Class.forName(全类名)适用于动态配置文件加载类名.class最为安全适用于编译期已确定类型的场景对象.getClass()适用于已知对象实例的场景。反射可打破封装限制通过调用setAccessible(true)反射可以访问和操作包括private在内的私有构造方法、字段和方法。反射是框架设计的基石虽然反射增加了运行时的灵活性如 Spring IoC、MyBatis 映射但过度使用反射会带来性能开销并且破坏了面向对象的封装性。