【无标题】

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. 对比总结

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

相关推荐
唐青枫7 小时前
Java JDBC 实战指南:从 Connection 到事务和连接池
java
一个做软件开发的牛马8 小时前
MyBatis-Plus 从零实战:完整搭建可运行 Demo,BaseMapper 零 SQL、Wrapper 条件构造、分页插件与代码生成器详解
java·后端
用户3721574261358 小时前
Java 处理 PDF 图片:提取 PDF 中的图片,并压缩 PDF 图片体积
java
用户3721574261358 小时前
Java 打印 Word 文档:从基础打印到高级设置
java
用户3521802454751 天前
当 Prompt 学会"热更新":Spring Boot × Nacos3 AI 实战
java·spring boot·ai编程
东坡白菜1 天前
破局全栈:一个前端开发的Java入门实战记录(1)
java·全栈
唐青枫1 天前
Java Tomcat 实战指南:从 Servlet 容器到 Spring Boot 部署
java
wsaaaqqq1 天前
roudan:自由选择实体、灵活操作数据、快速写入数据库的 Java 框架
java
plainGeekDev1 天前
null 判断 → Kotlin 可空类型
android·java·kotlin
糖拌西瓜皮1 天前
Java开发者视角:深入理解Node.js异步编程模型
java·后端·node.js