如何快速掌握 Java 反射之获取类的字段?

哈喽,各位小伙伴们,你们好呀,我是喵手。运营社区:掘金/C站/腾讯云/阿里云/华为云/51CTO(全网同号);欢迎大家常来逛逛,互相学习。

今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互相学习,一个人虽可以走的更快,但一群人可以走的更远。

我是一名后端开发爱好者,工作日常接触到最多的就是Java语言啦,所以我都尽量抽业余时间把自己所学到所会的,通过文章的形式进行输出,希望以这种方式帮助到更多的初学者或者想入门的小伙伴们,同时也能对自己的技术进行沉淀,加以复盘,查缺补漏。

小伙伴们在批阅的过程中,如果觉得文章不错,欢迎点赞、收藏、关注哦。三连即是对作者我写作道路上最好的鼓励与支持!

前言

上一期我们提到过反射(Reflection),给大家讲解的是它的一个重要特性:动态代理。学过 Java 的小伙伴都知道,反射是Java中的一项强大的功能,它允许程序在运行时动态地获取类的信息、访问类的构造方法、字段、方法等。Java 的反射机制使得我们能够在运行时检查和操作类的属性和行为,通常用于框架、库、工具等需要动态处理对象的场景,对吧(温习一遍)。

而在这篇文章中,我们将深入探讨 如何在Java 中运用反射机制进行"字段访问"?带着大家学习如何使用反射功能来访问和操作类的字段,包括私有字段、公共字段,以及如何通过反射对字段值进行修改,听起来是不是非常的cool!没错,我只教流批的玩意!

1. Java 反射机制概述

在 Java 中,反射是通过 java.lang.reflect 包来提供类和方法来实现的。反射机制能够让我们获取类的信息(如类名、父类、接口等),并能动态地操作类的实例。

常见的反射相关类有:

  • Class:用于获取类的信息。
  • Field:用于访问类的字段(成员变量)。
  • Method:用于访问类的方法。
  • Constructor:用于访问类的构造方法。

通过反射,我们可以绕过访问修饰符(如 privateprotected 等)来访问类的成员,甚至动态地创建和操作类的实例。那废话不多讲,我们直接实操,手把手教学!

2. 获取类的字段

2.1 如何获取字段对象?

反射中,字段被表示为 Field 类的实例。要获取类的字段,首先需要通过 Class 对象来获取相应的字段信息,如何理解呢?接下来我给大家简单写个案例,演示一波你们就明白了。

首先随意定义一个带属性的对象,比如它有name属性、age属性等。示例代码如下:

java 复制代码
/**
 * @Author 喵手
 * @date: 2025-04-15
 */
public class Person {
    private String name;
    public int age;

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

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}

接着,我们通过反射获取该对象的属性值,看看究竟要如何获取?示例代码如下:

java 复制代码
import java.lang.reflect.Field;

/**
 * @Author 喵手
 * @date: 2025-04-15
 */
public class ReflectionExample {
    public static void main(String[] args) throws NoSuchFieldException {
        Class<?> personClass = Person.class;

        // 获取 public 字段 age
        Field ageField = personClass.getField("age");
        System.out.println("Field: " + ageField.getName());  // 输出:age

        // 获取 private 字段 name
        Field nameField = personClass.getDeclaredField("name");
        System.out.println("Field: " + nameField.getName());  // 输出:name
    }
}

运行效果展示如下:

可以看到,属性被正常获取。

2.2 如何获取类中的所有字段?

这里可以使用 getDeclaredFields() 方法可以获取类中所有的字段,包括私有字段、保护字段和公共字段。

java 复制代码
import java.lang.reflect.Field;

/**
 * @Author 喵手
 * @date: 2025-04-15
 */
public class ReflectionExample1 {

    public static void main(String[] args) {
        Class<?> personClass = Person.class;

        // 获取所有字段(包括私有字段)
        Field[] fields = personClass.getDeclaredFields();
        for (Field field : fields) {
            System.out.println("Field: " + field.getName());
        }
    }
}

预期输出:

json 复制代码
Field: name
Field: age

运行效果展示如下:

2.3 如何访问私有字段?

要访问类中的私有字段,需要先通过 setAccessible(true) 来取消 Java 的访问控制检查。这是因为默认情况下,Java 会保护私有字段不被外部访问。通过调用 setAccessible(true),我们可以突破 Java 的访问权限限制,允许反射访问私有字段。

java 复制代码
import java.lang.reflect.Field;

/**
 * @Author 喵手
 * @date: 2025-04-15
 */
public class ReflectionExample2 {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        Person person = new Person("喵手", 25);
        Class<?> personClass = person.getClass();

        // 获取 private 字段 name
        Field nameField = personClass.getDeclaredField("name");

        // 取消 Java 的访问权限检查
        nameField.setAccessible(true);

        // 获取字段的值
        String name = (String) nameField.get(person);
        System.out.println("Name: " + name);  // 输出:喵手

        // 修改字段的值
        nameField.set(person, "喵手大佬");
        System.out.println("Modified Name: " + person.getName());  // 输出:喵手大佬
    }
}

预期输出:

json 复制代码
Name: 喵手
Modified Name: 喵手大佬

实际运行结果展示如下:

代码解析:

如上案例代码演示了如何通过 Java 反射机制访问和修改一个类的私有字段。如下是实现思路步骤:

  1. 创建对象 :首先,创建了一个 Person 类的对象 person,并初始化其 nameage 字段。

  2. 获取 Class 对象 :通过 person.getClass() 获取到 Person 类的 Class 对象,之后可以用反射操作该类的成员。

  3. 获取私有字段 :通过 personClass.getDeclaredField("name") 获取 Person 类中的私有字段 name。这时 name 字段虽然是 private,但反射允许我们访问。

  4. 取消访问权限检查 :使用 nameField.setAccessible(true) 取消 Java 默认的访问权限限制,允许访问私有字段。

  5. 读取字段值 :通过 nameField.get(person) 获取 person 对象中 name 字段的值,并输出。

  6. 修改字段值 :通过 nameField.set(person, "喵手大佬") 修改 person 对象中 name 字段的值,修改后的值可以通过调用 person.getName() 查看。

2.4 如何访问静态字段?

静态字段属于类而不是实例,因此访问静态字段时,反射的使用方法稍有不同。我们可以通过 getField()getDeclaredField() 来获取静态字段,并使用 Field 对象的 get() 方法来获取字段的值。

java 复制代码
import java.lang.reflect.Field;

/**
 * @Author 喵手
 * @date: 2025-04-15
 */
public class ReflectionExample3 {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        Class<?> peopleClass = People.class;

        // 获取静态字段 species
        Field speciesField = peopleClass.getField("species");

        // 获取静态字段的值
        String speciesValue = (String) speciesField.get(null);  // 静态字段不依赖于对象
        System.out.println("Species: " + speciesValue);  // 输出:哈咯,我是喵手!

        // 修改静态字段的值
        speciesField.set(null, "哈咯,我是帅哥!");
        System.out.println("Modified Species: " + speciesField.get(null));  // 输出:哈咯,我是帅哥
    }
}

预期输出:

json 复制代码
Species: 哈咯,我是喵手!
Modified Species: 哈咯,我是帅哥!

实际运行结果展示如下:

代码解析:

如上案例代码演示了如何通过 Java 反射访问和修改类的静态字段。如下是实现思路步骤:

  1. 获取 Class 对象 :通过 People.class 获取到 People 类的 Class 对象,可以用来操作该类的成员。

  2. 获取静态字段 :通过 peopleClass.getField("species") 获取 People 类中的公有静态字段 species。注意,这里使用的是 getField(),它只能获取 public 修饰的字段。如果字段是 private 或其他访问修饰符,需要使用 getDeclaredField()

  3. 获取静态字段的值 :通过 speciesField.get(null) 获取静态字段 species 的值。对于静态字段,get() 方法的参数可以传入 null,因为静态字段不依赖于具体的实例对象。

  4. 修改静态字段的值 :通过 speciesField.set(null, "哈咯,我是帅哥!") 修改静态字段 species 的值。同样,传入 null 作为第一个参数,因为字段是静态的,不依赖于具体对象。

  5. 输出修改后的值 :再次通过 speciesField.get(null) 获取修改后的静态字段值,并输出。

这段代码的关键点是:静态字段是属于类的,而不是某个特定实例,因此使用反射时在操作静态字段时不需要指定对象实例,直接传入 null 即可。

2.5 如何修改字段的值?

通过反射,我们不仅可以访问字段,还可以修改字段的值。修改字段时需要注意以下几点:

  • 如果字段是 private,必须使用 setAccessible(true) 来绕过访问控制。
  • 必须确保所修改的字段类型与字段原本类型兼容。

例如,修改实例对象的字段值:

java 复制代码
import java.lang.reflect.Field;

/**
 * @Author 喵手
 * @date: 2025-04-15
 */
public class ReflectionExample4 {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        Person person = new Person("喵手", 25);
        Class<?> personClass = person.getClass();

        // 获取 private 字段 name
        Field nameField = personClass.getDeclaredField("name");
        nameField.setAccessible(true);  // 取消访问控制

        // 修改字段的值
        nameField.set(person, "帅哥");
        System.out.println("Updated Name: " + person.getName());  // 输出:帅哥
    }
}

预期输出:

json 复制代码
Updated Name: 帅哥

实际运行结果展示如下:

代码解析:

如上案例代码演示了如何使用 Java 反射修改类的私有字段值。如下是实现思路步骤:

  1. 创建对象 :首先,创建了一个 Person 类的实例 person,并初始化 name"喵手"age25

  2. 获取 Class 对象 :通过 person.getClass() 获取 Person 类的 Class 对象,这个对象代表了 Person 类的元数据,接下来通过它来操作该类的成员。

  3. 获取私有字段 :通过 personClass.getDeclaredField("name") 获取 Person 类中的私有字段 name。由于该字段是私有的,反射默认是无法访问的。

  4. 取消访问控制 :通过 nameField.setAccessible(true) 来取消 Java 的访问控制限制,使得即使 name 字段是私有的,也可以在反射中进行操作。

  5. 修改字段值 :通过 nameField.set(person, "帅哥") 修改 person 对象中的 name 字段的值。这里的 person 是目标对象,"帅哥" 是新值。

  6. 输出修改后的值 :通过 person.getName() 输出修改后的 name 字段的值,确认修改成功。

3. Class类相关方法解析

3.1 getFields()

getFields()java.lang.Class 类的一个方法,用于获取当前类及其父类中所有 公共字段public 修饰符的字段)。与 getDeclaredFields() 不同,getFields() 只会返回类的 public 字段,并且会包括父类中继承的字段。

方法签名

java 复制代码
public Field[] getFields() throws SecurityException

返回值

  • 返回一个 Field[] 数组,其中包含了当前类及其所有父类声明的 public 字段。

主要特点

  1. 仅包含公共字段getFields() 只返回当前类及其父类中 public 修饰符声明的字段,不包括 privateprotected 或默认访问权限的字段。
  2. 包含父类字段getFields() 会返回当前类和父类中继承下来的 public 字段。如果父类有 public 字段,子类也可以通过 getFields() 获取。
  3. 访问控制 :与 getDeclaredFields() 不同,getFields() 返回的字段会根据其可见性进行过滤,只有 public 字段才能被获取。

3.2 getDeclaredFields()

getDeclaredFields()Class 类的一个方法,用于获取某个类(包括父类)中所有声明的字段(包括私有字段、保护字段和公共字段)。

方法签名

java 复制代码
public Field[] getDeclaredFields() throws SecurityException

返回值

  • 返回一个 Field[] 数组,其中包含了该类声明的所有字段(不包括继承自父类的字段)。
  • 每个 Field 对象表示类中一个声明的字段。

主要特点

  1. 包括所有字段getDeclaredFields() 会返回当前类中所有的字段,包括:

    • 私有字段private
    • 保护字段protected
    • 公共字段public
    • 默认访问权限字段 (没有 publicprotectedprivate 修饰符的字段)
  2. 不包含父类字段 :如果当前类继承了父类,getDeclaredFields() 只返回当前类声明的字段 ,不会包括父类的字段。要获取所有字段(包括父类的字段),可以使用 getFields()

  3. 不检查访问权限 :通过 getDeclaredFields() 获取的字段对象,不会检查字段的访问修饰符。如果字段是 private 或者是 protected,你依然可以通过 Field 对象进行访问,只不过可能需要通过 setAccessible(true) 来绕过 Java 的访问控制检查。

设置字段可访问

如果你想访问 privateprotected 字段,需要调用 setAccessible(true) 方法来绕过 Java 的访问控制检查。

getFields() 的区别

  • getDeclaredFields():返回所有由类声明的字段,不考虑访问权限,不包括继承自父类的字段。
  • getFields():只返回当前类及其父类中 public 修饰的字段,无法获取 privateprotected 字段。

4. 总结

Java 反射,提供了一种动态访问和操作类字段的方式,能够突破 Java 的访问权限限制,例如:访问 private 字段、静态字段等。常用的反射操作包括:

  • 获取字段 :使用 getDeclaredField()getField() 获取字段。
  • 访问字段的值 :使用 Field.get() 方法访问字段的值。
  • 修改字段的值 :使用 Field.set() 方法修改字段的值。
  • 访问静态字段 :静态字段是属于类的,因此可以通过 null 来表示该字段不依赖于实例。

通过反射,我们可以更加灵活地处理对象的成员,尤其是在动态创建和操作类实例、框架设计、序列化等方面,反射提供了极大的便利。然而,反射也带来了性能开销,因此应根据实际需求合理使用反射。所以说,并不能一味说反射就是最牛的,各有利弊,需恰当适用才是最强的。

... ...

文末

好啦,以上就是我这期的全部内容,如果有任何疑问,欢迎下方留言哦,咱们下期见。

... ...

学习不分先后,知识不分多少;事无巨细,当以虚心求教;三人行,必有我师焉!!!

wished for you successed !!!


⭐️若喜欢我,就请关注我叭。

⭐️若对您有用,就请点赞叭。

⭐️若有疑问,就请评论留言告诉我叭。


版权声明:本文由作者原创,转载请注明出处,谢谢支持!

相关推荐
小徐Chao努力12 分钟前
【解析】ReentrantLock锁、Syschronized锁面试点解析
java·面试·职场和发展·synchronized·
楽码14 分钟前
一文看懂!编程语言访问变量指针和复制值
后端·go·编程语言
逝水年华QAQ20 分钟前
告别混乱与重装烦恼,一款Windows 软件管理神器!
windows·后端
lcf_zhangxing20 分钟前
提升 Odoo 开发效率:Ubuntu 22.04 + Makefile 的最佳实践
后端
V少年22 分钟前
深入浅出Java线程池
android·java
Weison25 分钟前
Apache Doris 性能优化补丁:近期版本更新概览
后端
顾林海29 分钟前
深度解析Hashtable工作原理
android·java·面试
只恨天高31 分钟前
SpringBoot异常处理之注解@ExceptionHander和@ControllerAdvice
java·spring boot·spring
都叫我大帅哥35 分钟前
Java中的Collection必知必会
java
fliter37 分钟前
性能比拼: Actix (Rust) vs Zap (Zig) vs Zig
后端