一篇文章搞懂 java 反射

一篇文章搞懂 java 反射

简述

Java 反射机制是指在程序运行状态中,对于任意一个类,都能获取其所有属性和方法;对于任意一个对象,都能调用其任意方法和属性。这种动态获取信息与动态调用对象方法的能力,就是 Java 反射机制的核心。

反射基础

  • RTTI(运行时类型识别) :区别于编译时已确定的类型,RTTI 能在运行时识别对象或类的信息,而反射是 RTTI 的重要实现方式。
  • 反射的本质:将类的属性、方法、构造器等组件 "解剖" 为一个个可操作的对象,从而实现动态操作类的能力。

Class 类

Java 中每个类或接口在 JVM 中都对应一个Class对象(包括基本数据类型和 void),它是反射的核心入口。

Class 类的特点

  • java.lang包下的 final 类,实现了多个接口。

  • 构造方法为private,仅由 JVM 创建和加载,开发者无法手动实例化。

  • 每个类在 JVM 中仅有一个对应的Class对象,全局唯一。

kotlin 复制代码
public final class Class<T> implements java.io.Serializable,
                              GenericDeclaration,
                              Type,
                              AnnotatedElement {
    // 源码片段:构造方法私有
    private Class(ClassLoader loader) {
        classLoader = loader;
    }
}

类加载机制

  1. 编译 :Java 文件编译为.class文件,包含类的完整信息。

  2. 加载 :类加载器(ClassLoader)将.class文件加载到内存。

  3. 生成 Class 对象 :JVM 为每个类创建唯一的Class对象,后续所有实例对象都基于该Class对象创建。

示意图逻辑

本地 / 网络的.class文件 → 类加载器 → 内存生成唯一Class对象 → 基于Class对象创建多个实例对象。

反射的核心类库

Java 反射主要依赖java.lang.Class类和java.lang.reflect包,其中关键类包括:

  • Constructor:类的构造方法
  • Field:类的成员变量
  • Method:类的成员方法
  • Modifier:访问修饰符工具类

常用方法速查表

方法 说明
forName(String) 通过全限定类名获取Class对象,立即加载并初始化类
getClass() 通过对象实例获取Class对象,返回实际类型
getName() 获取类的全限定名(含包名)
getSimpleName() 获取类名(不含包名)
isXXX() 判断类型(如isInterface()是否为接口,isEnum()是否为枚举)
getXXX() 获取单个成员(如getField(String)获取 public 字段),含父类 public 成员
getXXXs() 获取成员数组(如getMethods()获取所有 public 方法),含父类 public 成员
getDeclaredXXX() 获取本类声明的单个成员(含私有),不含父类
getDeclaredXXXs() 获取本类声明的所有成员(含私有),不含父类
newInstance() 通过无参构造器创建实例(需类有默认构造器)

获取 Class 对象的三种方式

  1. 类名.class:通过类名直接获取,编译时确定类型。

  2. 对象.getClass () :通过实例对象获取,运行时确定实际类型。

  3. Class.forName (全限定类名) :通过类路径动态获取,适用于未知类型。

java 复制代码
// 示例
public class ReflectionDemo {
    public static void main(String[] args) throws ClassNotFoundException {
        // 方式1:类名.class
        Class<Student> clazz1 = Student.class;
        
        // 方式2:对象.getClass()
        Student student = new Student();
        Class<? extends Student> clazz2 = student.getClass();
        
        // 方式3:Class.forName()
        Class<?> clazz3 = Class.forName("com.example.Student");
    }
}

Constructor 类:操作构造方法

Constructor类用于获取和调用类的构造方法,支持访问私有构造器。

示例代码

arduino 复制代码
public class Student {
    private String name;
    public int age;

    public Student() {}
    public Student(int age) { this.age = age; }
    private Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

// 反射操作构造器
public class TestConstructor {
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("com.example.Student");
        
        // 调用无参构造器
        Student s1 = (Student) clazz.newInstance();
        
        // 调用public单参构造器
        Constructor<?> c1 = clazz.getConstructor(int.class);
        Student s2 = (Student) c1.newInstance(18);
        
        // 调用私有构造器(需设置可访问)
        Constructor<?> c2 = clazz.getDeclaredConstructor(String.class, int.class);
        c2.setAccessible(true); // 解除私有访问限制
        Student s3 = (Student) c2.newInstance("Tom", 20);
    }
}

Field 类:操作成员变量

Field类用于获取和修改类的成员变量,包括私有变量。

示例代码

csharp 复制代码
public class TestField {
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Student.class;
        Student student = new Student();
        
        // 获取public字段(含父类)
        Field ageField = clazz.getField("age");
        ageField.set(student, 20); // 修改public字段
        System.out.println(ageField.get(student)); // 输出:20
        
        // 获取私有字段(仅本类)
        Field nameField = clazz.getDeclaredField("name");
        nameField.setAccessible(true); // 解除私有限制
        nameField.set(student, "Jerry");
        System.out.println(nameField.get(student)); // 输出:Jerry
    }
}

Method 类:操作成员方法

Method类用于获取和调用类的成员方法,包括私有方法。

示例代码

csharp 复制代码
public class Student {
    private void showName() {
        System.out.println("Name: " + name);
    }
    public int getAge() {
        return age;
    }
}

// 反射操作方法
public class TestMethod {
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Student.class;
        Student student = new Student();
        
        // 调用私有方法
        Method showMethod = clazz.getDeclaredMethod("showName");
        showMethod.setAccessible(true);
        showMethod.invoke(student); // 输出:Name: Jerry
        
        // 调用public方法
        Method getMethod = clazz.getMethod("getAge");
        int age = (int) getMethod.invoke(student);
        System.out.println("Age: " + age); // 输出:20
    }
}

反射的实际应用:Spring IOC

Spring 的 IOC 容器核心依赖反射实现:

  1. 扫描类路径下的@Component注解类,通过Class.forName()获取Class对象。
  2. 解析类的属性,若存在@Autowired注解,通过Field类设置属性可访问(setAccessible(true))。
  3. 自动注入依赖的 Bean 实例,实现 "控制反转"。

总结

反射是 Java 动态编程的核心技术,通过操作ClassConstructorFieldMethod等类,可在运行时灵活操作类的成员。尽管反射会牺牲部分性能并打破封装性,但在框架开发(如 Spring、MyBatis)中不可或缺。掌握反射,能更深入理解 Java 的底层机制和框架原理。

相关推荐
追逐时光者1 小时前
精选 5 款 .NET 开源、功能强大的工作流系统,告别重复造轮子!
后端·.net
帅得不敢出门1 小时前
Android Framework定制长按电源键关机的窗口
android·java·framework
fatfishccc1 小时前
循序渐进学 Spring (上):从 IoC/DI 核心原理到 XML 配置实战
xml·java·数据库·spring·intellij-idea·ioc·di
bobz9652 小时前
Agent AI:多模态交互前沿调查
后端
蒋星熠2 小时前
Rust 异步生态实战:Tokio 调度、Pin/Unpin 与零拷贝 I/O
人工智能·后端·python·深度学习·rust
公众号_醉鱼Java2 小时前
Elasticsearch文档数迷思:深度解析count与stats背后机制
后端
勇往直前plus2 小时前
一文学习nacos和openFeign
java·学习·微服务·openfeign
Warren982 小时前
公司项目用户密码加密方案推荐(兼顾安全、可靠与通用性)
java·开发语言·前端·javascript·vue.js·python·安全
David爱编程2 小时前
Java 编译期 vs 运行期:避开这些坑,少掉一半 Bug
java·后端