【无标题】

Java反射机制入门

1. 引入:为什么需要反射?

1.1 正常创建对象的局限性

正常情况下,我们创建对象是这样的:

java 复制代码
Student s = new Student();
s.study();

问题 :这种写法在编译时就必须知道类的全名。如果类的名字是在程序运行时才知道(比如从配置文件读取),怎么办?

properties 复制代码
# config.properties
className = com.example.Student
1.2 反射的解决方案

反射 允许程序在运行时动态地获取类的信息、创建对象、调用方法。

java 复制代码
// 运行时才知道要创建哪个类
String className = 从配置文件读取到的类名;
Class clazz = Class.forName(className);  // 获取类对象
Object obj = clazz.newInstance();        // 创建实例

核心理解:反射就是把Java类本身当成对象来操作。


2. 类的加载机制

2.1 类加载的三个阶段
复制代码
编写代码 → 编译 → 加载 → 连接 → 初始化 → 使用 → 卸载
          ↓
      .java  .class
阶段 说明
加载 将.class文件读入内存,创建Class对象
连接 验证、准备(分配内存给静态变量)、解析(符号引用转直接引用)
初始化 执行静态代码块和静态变量赋值
2.2 类加载器(ClassLoader)

类加载器负责把.class文件加载到内存:

java 复制代码
// 获取类加载器
ClassLoader loader = Student.class.getClassLoader();
System.out.println(loader);  // AppClassLoader

// 类加载器的层级
// BootstrapClassLoader(根加载器,加载rt.jar)
//      ↑
// ExtensionClassLoader(扩展加载器)
//      ↑
// AppClassLoader(应用加载器,加载自己写的类)
2.3 类加载的时机

类第一次被使用时才会加载:

  • new对象时
  • 调用静态方法/静态变量时
  • 反射调用时
java 复制代码
// 下面这行代码会触发Student类加载
Student s = new Student();

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

方式 代码 适用场景
类名.class Class c = Student.class; 已知类型
对象.getClass() Class c = s.getClass(); 已有对象
Class.forName() Class c = Class.forName("com.example.Student"); 只知道全类名(最常用)
java 复制代码
public class GetClassDemo {
    public static void main(String[] args) throws Exception {
        // 方式1:类名.class
        Class c1 = Student.class;
        
        // 方式2:对象.getClass()
        Student s = new Student();
        Class c2 = s.getClass();
        
        // 方式3:Class.forName()(最灵活)
        Class c3 = Class.forName("com.Student");
        
        System.out.println(c1 == c2);  // true
        System.out.println(c1 == c3);  // true(同一个Class对象)
    }
}

重点:同一个类在JVM中只存在一个Class对象,三种方式获取的是同一个。


4. 获取类的结构信息

4.1 获取构造方法
java 复制代码
import java.lang.reflect.Constructor;

public class ConstructorDemo {
    public static void main(String[] args) throws Exception {
        Class clazz = Student.class;
        
        // 获取所有public构造方法
        Constructor[] constructors = clazz.getConstructors();
        
        // 获取所有构造方法(包括private)
        Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
        
        // 获取指定参数的public构造方法
        Constructor c1 = clazz.getConstructor(String.class, int.class);
        
        // 获取指定参数的构造方法(包括private)
        Constructor c2 = clazz.getDeclaredConstructor(String.class);
        
        //获取构造方法的修饰符号
        int modefiy = c2.getModifiers();
        
        //获取所有参数
        Parameter[] parameters = c2.getParameters();
        
        // 通过构造方法创建对象
        Student s = (Student) c1.newInstance("张三", 18);
        
        // 如果是private构造方法,需要先设置可访问
        c2.setAccessible(true);
        Student s2 = (Student) c2.newInstance("李四");
    }
}
4.2 获取成员变量
java 复制代码
import java.lang.reflect.Field;

public class FieldDemo {
    public static void main(String[] args) throws Exception {
        Class clazz = Student.class;
        
        // 获取所有public成员变量
        Field[] fields = clazz.getFields();
        
        // 获取所有成员变量(包括private)
        Field[] declaredFields = clazz.getDeclaredFields();
        
        // 获取指定的public成员变量
        Field field1 = clazz.getField("name");
        
        // 获取指定的成员变量(包括private)
        Field field2 = clazz.getDeclaredField("age");
        
        //获取成员修饰符
        int modifiy = field2.getModifiers();
        
        //获取名字
        String name = field2.getName();
        
        //获取数据类型
        Class<?> type = field2.getType();
        
        // 操作成员变量
        Student s = new Student("张三", 18);
        
        // 获取值
        Object name = field1.get(s);
        
        // 设置值
        field1.set(s, "李四");
        
        // private变量需要先设置可访问
        field2.setAccessible(true);
        field2.set(s, 20);  // 给age赋值
    }
}
4.3 获取成员方法
java 复制代码
import java.lang.reflect.Method;

public class MethodDemo {
    public static void main(String[] args) throws Exception {
        Class clazz = Student.class;
        
        // 获取所有public方法(包括父类的)
        Method[] methods = clazz.getMethods();
        
        // 获取所有方法(包括private,不包括父类)
        Method[] declaredMethods = clazz.getDeclaredMethods();
        
        // 获取指定的public方法
        Method method1 = clazz.getMethod("study", String.class);
        
        //获取方法修饰符
        int modefiers = method1.getModifiers();
        
        //获取方法名字
        String name = method1.getName();
        
        //获取所有参数
        Parameter[] parameters = method1.getParameters();
        
        // 获取指定的方法(包括private)
        Method method2 = clazz.getDeclaredMethod("privateMethod");
        
        //获取方法抛出的异常
        Class[] = exceptions = method1.getExceptionTypes();
        
        // 调用方法
        Student s = new Student("张三", 18);
        
        // 调用public方法
        Object result = method1.invoke(s, "语文");  // 相当于 s.study("语文")
        
        // 调用private方法
        method2.setAccessible(true);
        method2.invoke(s);
        
        // 调用静态方法(不需要传对象)
        Method staticMethod = clazz.getMethod("staticMethod");
        staticMethod.invoke(null);
    }
}
4.4 获取其他信息
java 复制代码
public class OtherInfoDemo {
    public static void main(String[] args) {
        Class clazz = Student.class;
        
        // 获取类名
        String className = clazz.getName();           // 全类名:com.example.Student
        String simpleName = clazz.getSimpleName();    // 简单类名:Student
        
        // 获取包名
        Package pkg = clazz.getPackage();
        String pkgName = pkg.getName();               // com.example
        
        // 获取父类
        Class superClass = clazz.getSuperclass();     // Person.class
        
        // 获取实现的接口
        Class[] interfaces = clazz.getInterfaces();   // [Comparable.class, Serializable.class]
        
        // 判断类型
        boolean isArray = clazz.isArray();            // 是否是数组
        boolean isInterface = clazz.isInterface();    // 是否是接口
        boolean isEnum = clazz.isEnum();              // 是否是枚举
    }
}

5. 反射常用API总结

5.1 Class类的方法
方法 说明
Class.forName(String) 获取Class对象
newInstance() 调用无参构造创建对象(已过时)
getConstructor(Class...) 获取public构造方法
getDeclaredConstructor(Class...) 获取指定构造方法
getMethod(String, Class...) 获取public方法
getDeclaredMethod(String, Class...) 获取指定方法
getField(String) 获取public成员变量
getDeclaredField(String) 获取指定成员变量
getName() 获取全类名
getSuperclass() 获取父类
5.2 Constructor类的方法
方法 说明
newInstance(Object...) 创建对象
setAccessible(boolean) 设置可访问(突破private)
5.3 Method类的方法
方法 说明
invoke(Object, Object...) 调用方法(传对象和参数)
setAccessible(boolean) 设置可访问
getName() 获取方法名
5.4 Field类的方法
方法 说明
get(Object) 获取成员变量值
set(Object, Object) 设置成员变量值
setAccessible(boolean) 设置可访问
getType() 获取变量类型

6. 反射的应用场景

场景 说明
框架开发 Spring、MyBatis通过反射创建对象、注入属性
配置文件驱动 从配置文件读取类名,动态创建
注解处理 读取注解信息,执行相应逻辑
调试工具 查看任意对象的结构

7. 反射的优缺点

优点 缺点
动态性,运行时决定行为 性能较低(比直接调用慢)
框架的基础 破坏封装(private可访问)
高度灵活性 代码可读性下降
解耦 安全性降低

8. 易错点总结

  1. Class.forName()要写全类名 :包含包名,如com.example.Student

  2. newInstance()已过时 :Java 9后用getDeclaredConstructor().newInstance()

  3. private成员需要setAccessible(true) :否则会报IllegalAccessException

  4. invoke静态方法时对象参数传nullmethod.invoke(null, 参数)

  5. 基本类型反射要用Typeint.class而不是Integer.class

  6. 性能问题:高频调用的代码避免使用反射,或使用缓存


9. 对比总结

维度 正常调用 反射调用
时机 编译时 运行时
性能
灵活性
封装性 遵守 可破坏
代码安全
适用场景 业务代码 框架代码

相关推荐
宇宙realman_9996 小时前
420B污染度等级查询代码
java·开发语言·算法
小白学大数据6 小时前
Playwright 爬虫:Python 爬取 JS 渲染的 JSP 网站
开发语言·javascript·爬虫·python·数据分析
折哥的程序人生 · 物流技术专研6 小时前
《Java 100 天进阶之路》第35篇:Java异常处理最佳实践
java·开发语言·后端·面试·求职招聘
AI玫瑰助手6 小时前
Python函数:位置参数与关键字参数的使用
开发语言·python·信息可视化
如竟没有火炬6 小时前
乘法表中第K小的数——二分
开发语言·数据结构·python·算法·leetcode·职场和发展·动态规划
凯瑟琳.奥古斯特6 小时前
选择题专练数据库原理精选30题
开发语言·数据库·职场和发展·数据库开发
苦逼的猿宝7 小时前
洗衣店订单管理系统(源码+论文)
java·毕业设计·springboot·计算机毕业设计
ID_180079054737 小时前
小红书评论 API 接口详解与实战开发
java·jvm·c++