Java 反射详解

1. 反射概述

1.1 什么是反射

反射(Reflection)是 Java 提供的强大特性,允许程序在运行时动态地获取、访问类的所有信息(包括类名、属性、方法、构造器、注解等),并能动态操作这些信息,突破编译期的访问限制。

1.2 反射的作用

  • 获取类信息:运行时获取对象所属类的名称、包名、父类、实现的接口等;
  • 动态创建对象 :无需new关键字,通过类名字符串构造任意类的对象;
  • 访问成员变量:获取类的所有变量(包括私有、final 变量),并读写其值;
  • 调用方法:获取类的所有方法(包括私有方法),并动态调用;
  • 解析注解:运行时读取类 / 方法 / 属性上的注解,是框架实现注解驱动的核心。

2. Class 类:反射的根源

2.1 Class 类概述

JVM 加载完类后,会在堆内存的方法区中生成一个Class类型的对象 ------ 这个对象是该类的 "说明书",包含了类的所有信息(类名、父类、接口、属性、方法、构造器等)。

核心特点:

  • 每个类在 JVM 中只有一份字节码 ,对应唯一的Class对象;
  • Class对象是反射的入口,所有反射操作都基于Class对象展开;
  • 无论是自定义的Person类,还是 JDK 内置的String/ArrayList,加载后都会生成对应的Class对象。

2.2 获取 Class 对象的三种方式(附案例)

获取Class对象有三种核心方式,以下以Student类为例演示:

第一步:定义 Student 测试类

java

运行

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

    // 构造方法:私有、默认、公共(多参)
    public Student() {}
    private Student(String name) {this.name = name;}
    Student(String name, int age) {this.name = name; this.age = age;}
    public Student(String name, int age, String address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }

    // 成员方法:私有、公共(无参/有参/有返回值)
    private void function() {System.out.println("执行function()");}
    public void method1() {System.out.println("执行method1()");}
    public void method2(String s) {System.out.println("执行method2():" + s);}
    public String method3(String s, int i) {return "执行method3():"+s + "," + i;}

    @Override
    public String toString() {
        return "Student{" + "name='" + name + '\'' + ", age=" + age + ", address='" + address + '\'' + '}';
    }
}
第二步:获取 Class 对象的三种方式

java

运行

复制代码
public class ClassTest {
    public static void main(String[] args) throws ClassNotFoundException {
        // 方式1:类.class(编译期确定,最安全)
        System.out.println("---------方式1:类.class---------");
        Class<Student> c1 = Student.class;
        System.out.println(c1); // class com.hg.java.Student
        Class<Student> c2 = Student.class;
        System.out.println(c1 == c2); // true(同一Class对象)

        // 方式2:对象.getClass()(运行时获取,适用于已有对象的场景)
        System.out.println("---方式2:对象.getClass()---");
        Student s = new Student();
        Class<? extends Student> c3 = s.getClass();
        System.out.println(c1 == c3); // true

        // 方式3:Class.forName(全类名)(动态加载,最灵活,适用于配置文件场景)
        System.out.println("----方式3:Class.forName(全类名)----");
        Class<?> c4 = Class.forName("com.hg.java.Student");
        System.out.println(c1 == c4); // true
    }
}

3. Constructor:操作类的构造器

3.1 Constructor 类概述

java.lang.reflect.Constructor是反射体系中描述类的构造器的类:

  • 类的每个构造器(无参 / 有参、公有 / 私有)都对应一个Constructor对象;
  • 通过Constructor对象可在运行时创建类的实例,甚至调用私有构造器。

3.2 获取 Constructor 对象的方法(附案例)

方法 说明
Constructor<?>[] getConstructors() 返回所有公共构造方法的数组
Constructor<?>[] getDeclaredConstructors() 返回所有构造方法的数组(含私有)
Constructor getConstructor(Class<?>... parameterTypes) 返回单个公共构造方法(指定参数类型)
Constructor getDeclaredConstructor(Class<?>...parameterTypes) 返回单个构造方法(含私有,指定参数类型)
案例:获取 Student 类的构造器

java

运行

复制代码
public class ConstructorTest {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
        Class<?> c = Class.forName("com.hg.java1.Student");
        
        // 1. 获取所有构造器(含私有)
        System.out.println("---返回所有构造方法的数组----");
        Constructor<?>[] cons = c.getDeclaredConstructors();
        for (Constructor con : cons) {
            System.out.println(con);
        }

        // 2. 获取单个公共构造器
        System.out.println("-----返回单个构造方法----");
        Constructor<?> con = c.getConstructor(); // 无参构造
        System.out.println(con);
        con = c.getConstructor(String.class, int.class, String.class); // 三参构造
        System.out.println(con);           
    }
}

3.3 Constructor 常用方法(附案例)

核心方法:T newInstance(Object...initargs) ------ 根据指定构造器创建对象。

java

运行

复制代码
public class ConstructorTest {
    public static void main(String[] args) throws Exception {
        Class<?> c = Class.forName("com.hg.java1.Student");
        
        // 1. 通过无参构造创建对象
        System.out.println("------通过反射获取公共构造方法并创建对象--------");
        Constructor<?> con = c.getConstructor();
        Student s1 = (Student) con.newInstance();
        System.out.println(s1); // Student{name='null', age=0, address='null'}

        // 2. 通过三参构造创建对象
        con = c.getConstructor(String.class, int.class, String.class);
        Object obj = con.newInstance("林青霞", 30, "西安");
        System.out.println(obj); // Student{name='林青霞', age=30, address='西安'}
    }
}

4. Field:操作类的成员变量

4.1 Field 类概述

java.lang.reflect.Field是反射体系中描述类的成员变量的类:

  • 每个成员变量(公有 / 私有、静态 / 实例、final)都对应一个Field对象;
  • 通过Field对象可在运行时读写变量值,突破访问权限限制。

4.2 获取 Field 对象的方法(附案例)

方法 说明
Field[] getFields() 返回所有公共成员变量的数组
Field[] getDeclaredFields() 返回所有成员变量的数组(含私有)
Field getField(String name) 返回单个公共成员变量(指定变量名)
Field getDeclaredField(String name) 返回单个成员变量(含私有,指定变量名)
案例:获取 Student 类的成员变量

java

运行

复制代码
public class FieldTest {
    public static void main(String[] args) throws Exception {
        Class<?> c = Class.forName("com.hg.java1.Student");

        // 1. 获取所有公共成员变量
        System.out.println("---------返回所有公共成员变量的数组----------");
        Field[] fields = c.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }

        // 2. 获取所有成员变量(含私有)
        System.out.println("---------返回所有成员变量的数组----------");
        fields = c.getDeclaredFields();
        for (Field field : fields) {
            System.out.println(field);
        }

        // 3. 获取单个公共成员变量
        System.out.println("---------返回单个公共成员变量----------");
        Field addressField = c.getField("address");
        System.out.println(addressField);

        // 4. 获取单个私有成员变量
        System.out.println("---------返回单个私有成员变量----------");
        Field nameField = c.getDeclaredField("name");
        System.out.println(nameField);
    }
}

4.3 Field 常用方法(附案例)

核心方法:void set(Object obj,Object value) ------ 给指定对象的成员变量赋值。

java

运行

复制代码
public class FieldTest {
    public static void main(String[] args) throws Exception{
        Class<?> c = Class.forName("com.hg.java1.Student");
        // 先通过构造器创建对象
        Constructor<?> con = c.getConstructor();
        Object obj = con.newInstance();

        // 1. 给公共变量address赋值
        Field addressField = c.getField("address");
        addressField.set(obj, "西安");
        System.out.println(obj); // Student{name='null', age=0, address='西安'}

        // 2. 给私有变量age赋值(默认禁止访问,需先解除限制)
        Field ageField = c.getDeclaredField("age");
        ageField.setAccessible(true); // 解除访问权限检查
        ageField.set(obj, 30);
        System.out.println(obj); // Student{name='null', age=30, address='西安'}
    }
}

5. Method:操作类的成员方法

5.1 Method 类概述

java.lang.reflect.Method是反射体系中描述类的成员方法的类:

  • 每个方法(公有 / 私有、静态 / 实例、有参 / 无参)都对应一个Method对象;
  • 通过Method对象可在运行时调用方法,包括私有方法。

5.2 获取 Method 对象的方法(附案例)

方法 说明
Method[] getMethods() 返回所有公共成员方法的数组(含父类继承的)
Method[] getDeclaredMethods() 返回所有成员方法的数组(仅本类,含私有)
Method getMethod(String name, Class<?>... parameterTypes) 返回单个公共方法(指定方法名 + 参数类型)
Method getDeclaredMethod(String name, Class<?>... parameterTypes) 返回单个方法(含私有,指定方法名 + 参数类型)
案例:获取 Student 类的成员方法

java

运行

复制代码
public class MethodTest {
    public static void main(String[] args) throws Exception {
        Class<?> c = Class.forName("com.hg.java1.Student");

        // 1. 获取所有公共方法(含toString、equals等父类方法)
        System.out.println("-----------返回所有公共成员方法的数组--------");
        Method[] methods = c.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }

        // 2. 获取所有本类方法(含私有)
        System.out.println("-----------返回所有成员方法的数组--------");
        methods = c.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println(method);
        }

        // 3. 获取单个公共方法
        System.out.println("-----------返回单个公共成员方法--------");
        Method method = c.getMethod("method1"); // 无参
        System.out.println(method);
        Method method2 = c.getMethod("method2", String.class); // 单参
        System.out.println(method2);
        Method method3 = c.getMethod("method3", String.class, int.class); // 双参
        System.out.println(method3);
        
        // 4. 获取私有方法
        System.out.println("-----------返回单个非公共成员方法--------");
        Method method4 = c.getDeclaredMethod("function");
        System.out.println(method4);
    }
}

5.3 Method 常用方法(附案例)

核心方法:Object invoke(Object obj,Object... args) ------ 调用指定对象的方法,参数为args,返回值为方法执行结果。

java

运行

复制代码
public class MethodTest {
    public static void main(String[] args) throws Exception {
        Class<?> c = Class.forName("com.hg.java1.Student");
        // 创建对象
        Constructor<?> con = c.getConstructor();
        Object obj = con.newInstance();

        // 1. 调用无参公共方法
        Method method = c.getMethod("method1");
        method.invoke(obj); // 执行method1()

        // 2. 调用单参公共方法
        Method method2 = c.getMethod("method2", String.class);
        method2.invoke(obj, "林青霞"); // 执行method2():林青霞

        // 3. 调用有返回值的公共方法
        Method method3 = c.getMethod("method3", String.class, int.class);
        Object o = method3.invoke(obj, "林青霞", 30);
        System.out.println(o); // 执行method3():林青霞,30

        // 4. 调用私有方法(需解除访问限制)
        Method method4 = c.getDeclaredMethod("function");
        method4.setAccessible(true);
        method4.invoke(obj); // 执行function()
    }
}

6. Annotation:操作类的注解

6.1 Annotation 类概述

java.lang.annotation.Annotation是反射体系中描述注解的类:

  • 每个加在类 / 方法 / 属性上的注解,都对应一个Annotation对象;
  • 反射可运行时读取注解的属性值,判断元素是否被注解标记(框架核心用法);
  • 注解必须标注@Retention(RetentionPolicy.RUNTIME),否则反射无法读取。

6.2 获取 Annotation 对象的方法(附案例)

方法 说明
Annotation[] getAnnotations() 返回元素上的所有注解(含继承的)
Annotation[] getDeclaredAnnotations() 返回元素上的所有注解(仅本元素)
Annotation getAnnotation(Class<?> annoClass) 返回指定类型的注解(含继承的)
Annotation getDeclaredAnnotation(Class<?> annoClass) 返回指定类型的注解(仅本元素)
isAnnotationPresent(Class<A> annoClass) 判断元素是否被指定注解标记
案例:解析类 / 属性 / 方法上的注解

java

运行

复制代码
import java.lang.annotation.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.annotation.Annotation;

public class AnnotationTest {
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("com.hg.java1.SampleClass");

        // 1. 解析类上的注解
        System.out.println("----------解析类上的注解---------");
        if (clazz.isAnnotationPresent(CustomAnnotation.class)) {
            CustomAnnotation customAnnotation = clazz.getAnnotation(CustomAnnotation.class);
            System.out.println("类注解-name: " + customAnnotation.name());
            System.out.println("类注解-value: " + customAnnotation.value());
        }

        // 2. 解析属性上的注解
        System.out.println("----------解析属性上的注解---------");
        Field field = clazz.getDeclaredField("sampleField");
        if (field.isAnnotationPresent(CustomAnnotation.class)) {
            CustomAnnotation customAnnotation = field.getAnnotation(CustomAnnotation.class);
            System.out.println("属性注解-name: " + customAnnotation.name());
            System.out.println("属性注解-value: " + customAnnotation.value());
        }

        // 3. 解析方法上的注解
        System.out.println("----------解析方法上的注解---------");
        Method method = clazz.getMethod("getSampleField");
        if (method.isAnnotationPresent(CustomAnnotation.class)) {
            CustomAnnotation customAnnotation = method.getAnnotation(CustomAnnotation.class);
            System.out.println("方法注解-name: " + customAnnotation.name());
            System.out.println("方法注解-value: " + customAnnotation.value());
        }
    }
}

// 自定义注解(必须指定RUNTIME保留策略)
@Retention(RetentionPolicy.RUNTIME)
@interface CustomAnnotation {
    String name() default "这是name";
    String value() default "这是value";
}

// 测试类
@CustomAnnotation(name="SampleClass",  value = "Sample Class Annotation")
class SampleClass {
    @CustomAnnotation(name="sampleField", value = "Sample Field Annotation")
    private String sampleField;

    @CustomAnnotation(name="getSampleMethod",  value = "Sample Method Annotation")
    public String getSampleField() {
        return sampleField;
    }

    public void setSampleField(String sampleField) {
        this.sampleField = sampleField;
    }
}

7. 实战作业(3 道经典反射练习题)

作业 1:通过反射越过泛型检查,给 ArrayList<Integer>添加字符串

要求:反射突破泛型的编译期检查,往ArrayList<Integer>中添加字符串数据。

java

运行

复制代码
// 提示:泛型仅在编译期生效,运行时会被擦除,反射可直接调用add方法
ArrayList<Integer> intList = new ArrayList<>();
intList.add(100);
// 请补充反射代码,添加字符串"Hello Reflection"

作业 2:通过反射运行配置文件中指定类的指定方法

要求:使用Properties解析配置文件,反射调用指定类的指定方法。

  • 配置文件student.properties

    properties

    复制代码
    className=com.java.exer.Student
    methodName=study
  • Student 类: java

    运行

    复制代码
    public class Student {
        public void study() {
            System.out.println("好好学习天天向上");
        }
    }

作业 3:反射解析注解并动态调用方法

要求:

  1. 判断StudentController类是否添加@MyController注解;
  2. 若添加,则将标注@MyRequestMapping的方法存入HashMap
  3. 控制台输入方法名,调用对应的方法。
  • 自定义注解: java

    运行

    复制代码
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyController {
       String name() default "这是name";
       String value() default "这是value";
    }
    
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyRequestMapping {
       String name() default "这是name";
       String value() default "这是value";
    }
  • StudentController 类: java

    运行

    复制代码
    @MyController
    public class StudentController {
        @MyRequestMapping
        public void method1() {
            System.out.println("执行了method1()");
        }
    
        public void method2() {
            System.out.println("执行了method2()");
        }
    
        @MyRequestMapping
        public void method3() {
            System.out.println("执行了method3()");
        }
    }

总结

反射是 Java 中极具威力的特性,核心是通过 JVM 中的类元数据突破编译期限制,实现动态化编程。掌握ClassConstructorFieldMethodAnnotation的使用,是理解 Spring 等框架底层原理的关键。建议结合本文案例动手实践,再完成课后作业,彻底掌握反射的核心用法。

相关推荐
雨季6662 小时前
Flutter 三端应用实战:OpenHarmony “极简文本行数统计器”
开发语言·前端·flutter·ui·交互
HalvmånEver2 小时前
Linux:线程创建与终止上(线程五)
java·linux·jvm
m0_748233172 小时前
PHP版本演进:从7.x到8.x全解析
java·开发语言·php
qq_12498707532 小时前
基于springboot的林业资源管理系统设计与实现(源码+论文+部署+安装)
java·vue.js·spring boot·后端·spring·毕业设计·计算机毕业设计
雨季6662 小时前
Flutter 三端应用实战:OpenHarmony 简易“动态字体大小调节器”交互模式深度解析
开发语言·flutter·ui·交互·dart
zhengfei6112 小时前
精选的优秀法证分析工具和资源列表
开发语言·php
当战神遇到编程2 小时前
图书管理系统
java·开发语言·单例模式
indexsunny2 小时前
互联网大厂Java求职面试实战:Spring Boot微服务与Kafka消息队列应用解析
java·数据库·spring boot·微服务·面试·kafka·jpa
u0109272713 小时前
实时数据流处理
开发语言·c++·算法