一篇文章搞懂 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 的底层机制和框架原理。

相关推荐
克里斯蒂亚诺更新19 小时前
myeclipse的pojie
java·ide·myeclipse
迷藏49419 小时前
**eBPF实战进阶:从零构建网络流量监控与过滤系统**在现代云原生架构中,**网络可观测性**和**安全隔离**已成为
java·网络·python·云原生·架构
迷藏49419 小时前
**发散创新:基于Solid协议的Web3.0去中心化身份认证系统实战解析**在Web3.
java·python·web3·去中心化·区块链
qq_4335021819 小时前
Codex cli 飞书文档创建进阶实用命令 + Skill 创建&使用 小白完整教程
java·前端·飞书
IT_陈寒19 小时前
为什么我的Vite热更新老是重新加载整个页面?
前端·人工智能·后端
safestar201219 小时前
ES批量写入性能调优:BulkProcessor 参数详解与实战案例
java·大数据·运维·jenkins
还在忙碌的吴小二19 小时前
Harness 最佳实践:Java Spring Boot 项目落地 OpenSpec + Claude Code
java·开发语言·spring boot·后端·spring
风吹迎面入袖凉19 小时前
【Redis】Redis的五种核心数据类型详解
java·redis
三分恶19 小时前
支付江湖路—第一章:支付溯源——从贝壳到比特
后端
夕除19 小时前
javaweb--02
java·tomcat