首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >【JavaSE】反射机制 && Class类 && 反射具体操作详解

【JavaSE】反射机制 && Class类 && 反射具体操作详解

原创
作者头像
lirendada
发布2026-03-24 17:52:06
发布2026-03-24 17:52:06
1080
举报
文章被收录于专栏:JavaJava

Ⅰ. 概念与意义

Java 反射是一种机制,允许程序在运行时

  1. 查看类的信息(如属性、方法、构造器等)
  2. 动态创建对象
  3. 访问和修改属性
  4. 调用方法
  5. 操作泛型参数、注解等元信息

反射的意义和应用场景如下所示:

应用场景

描述

举例

框架设计核心

Spring、Hibernate、MyBatis 等全靠它

动态注入Bean、解析注解、AOP

通用工具类/库开发

不知道具体类时也能处理

JSON序列化、ORM字段映射

插件机制 / 动态加载类

运行时加载未编译进来的类

SPI机制、Class.forName()

动态代理 & AOP 编程

实现日志增强、权限控制等

JDK动态代理、AspectJ

测试 & Mock 框架

访问私有字段,模拟行为

JUnit, Mockito 等

简化开发 / 规避重复代码

构建泛型代码库

动态调用 setter/getter

使用反射的风险和代价如下所示:

风险/代价

描述

⏱ 性能开销

反射是解释型操作,慢于直接调用

🔒 安全问题

可访问私有字段/方法,绕过封装

🧩 可读性差

代码不直观,难调试

🧱 编译器检查失效

编译时不知道是否写错方法名或类型

Ⅱ. 反射相关的类与操作

类名

用途

Class类

代表类的实体,在运行的Java应用程序中表示类和接口

Field类

代表类的成员变量、属性

Method类

代表类的方法

Constructor类

代表类的构造方法

Annotation类

代表类的注解

一、Class类(一切反射的起点)

官方文档

Java 文件被编译后,生成了 .class 文件,JVM 此时会去解读这个 .class 文件,而 .class 文件也被 JVM 解析为一个对象,这个对象就是 java.lang.Class

通过 Java 的反射机制应用到这个实例,就可以去获得甚至去添加改变这个类的属性和动作,使得这个类成为一个动态的类。

一旦拿到了 Class 对象,比如:

代码语言:javascript
复制
Class<?> clazz = Person.class; // 具体获取方式看后面

可以做很多事,比如下面的操作:(剩下的查文档)

方法

用途

clazz.getName()

获取类名

clazz.getDeclaredFields()

获取所有字段(所有权限均可访问)

clazz.getDeclaredField(String name)

获取指定字段(所有权限均可访问)(下面方法也是类似)

clazz.getDeclaredMethods()

获取所有方法(所有权限均可访问)

clazz.getDeclaredConstructors()

获取构造器(所有权限均可访问)

clazz.newInstance()

创建对象(已废弃,推荐 getConstructor().newInstance())

clazz.getSuperclass()

获取父类

clazz.getInterfaces()

获取实现的接口

clazz.isInterface()

是否是接口

clazz.isAnnotationPresent(XXX.class)

是否被某注解标记

clazz.getLoader()

获得类的加载器

二、获取Class类的三种方式(Student类见附录)

方式

语法

说明

Class.forName("全限定名")

Class.forName("com.demo.Person")

常用于配置、动态加载

类静态变量 class

Person.class

编译期就能获得类信息

对象.getClass()

obj.getClass()

运行时获得对象的真实类型

代码语言:javascript
复制
public static void main(String[] args){
    // 1. 使用Class.forName(),注意要指定包名,并且最好捕捉异常
    Class<?> c1 = null;
    try {
        c1 = Class.forName("Reflection.Student");
    } catch(ClassNotFoundException e) {
        e.printStackTrace();
    }

    // 2. 使用指定类的class静态变量
    Class<?> c2 = Student.class;

    // 3. 使用对象实例的getClass()方法
    Student st = new Student();
    Class<?> c3 = st.getClass();

    // 一个类在JVM中只会有一个Class实例,因此c1、c2、c3都指向同一个Class实例
    System.out.println(c1 == c2); // true
    System.out.println(c2 == c3); // true
    System.out.println(c3 == c1); // true
}

☠注意事项:

  1. 一个类在 JVM 中只会有一个 Class 实例。不管你创建多少对象,它们的 .getClass() 是同一个。
  2. 泛型信息在运行时会被擦除,可以参考泛型笔记。
  3. Class.forName() 会触发类初始化,会执行静态代码块,慎用

三、创建对象的两种方式

① 直接调用 Class 中的 newInstance() 方法

第一种方式就是调用 Class 对象中的 newInstance() 方法,这种方法现在已经被淘汰了,原因如下所示:

  1. 只能调用 无参构造方法
  2. 如果构造方法不是 public,会抛异常
  3. 已被标记为 过时方法(从 JDK 9 起)
代码语言:javascript
复制
public static void main(String[] args) {
    try {
        // 1. 先获取Class对象
        Class c = Class.forName("Reflection.Student");

        // 2. 然后调用newInstance()方法创建对象,注意要强转成Student类型
        Student p = (Student)c.newInstance();
        System.out.println(p);

    } catch (ClassNotFoundException e) {
        throw new RuntimeException(e);
    } catch (InstantiationException e) {
        throw new RuntimeException(e);
    } catch (IllegalAccessException e) {
        throw new RuntimeException(e);
    }
}

② 生成 Constructor 构造方法对象

官方文档

第二种方式先调用 Class 对象中的 getConstructor() 系列的方法,得到 Constructor 对象,然后调用该对象中的 newInstance() 方法,优势如下所示:

  1. 可调用有参构造方法
  2. 可修改构造器可见性(setAccessible(true)
代码语言:javascript
复制
public static void main(String[] args) {
    try {
        // 1. 获取Class对象
        Class<?> cs = Class.forName("Reflection.Student");

        // 2. 获取无参的构造方法来创建对象
        Constructor<?> non_arg_constructor = cs.getDeclaredConstructor();
        Student s1 = (Student)non_arg_constructor.newInstance();
        System.out.println(s1);

        // 3. 获取带参的构造方法来创建对象,因为带参构造方法是私有的,所以要设置权限为true
        Constructor<?> arg_constructor = cs.getDeclaredConstructor(String.class, int.class);
        arg_constructor.setAccessible(true);
        Student s2 = (Student)arg_constructor.newInstance("liren", 18);
        System.out.println(s2);
        
    } catch (ClassNotFoundException e) {
        throw new RuntimeException(e);
    } catch (NoSuchMethodException e) {
        throw new RuntimeException(e);
    } catch (InvocationTargetException e) {
        throw new RuntimeException(e);
    } catch (InstantiationException e) {
        throw new RuntimeException(e);
    } catch (IllegalAccessException e) {
        throw new RuntimeException(e);
    }
}

// 运行结果:
Student()
Student{name='bit', age=18}
Student(String,name)
Student{name='liren', age=18}

四、操作类的字段(例子)

官方文档

代码语言:javascript
复制
public static void main(String[] args) {
    // 使用反射操作字段
    try {
        Class<?> cs = Class.forName("Reflection.Student");

        // 1. 输出类名,打印字段信息
        System.out.println("ClassName: " + cs.getName());
        for(Field e : cs.getDeclaredFields()) {
            System.out.println(e.getName() + " " + e.getType().getName() + " " + Modifier.toString(e.getModifiers()));
        }

        // 2.1 获取私有属性name
        Field namefield = cs.getDeclaredField("name");
        namefield.setAccessible(true); // 对于私有属性,需要设置Accessible为true

        // 2.2 获取该类对象,并设置name属性的值,然后用namefield.get()获取这个私有值
        Student st = (Student)cs.newInstance();
        namefield.set(st, "liren");
        System.out.println("反射私有属性修改了name:" + namefield.get(st));

    } catch (ClassNotFoundException | NoSuchFieldException e) {
        throw new RuntimeException(e);
    } catch (InstantiationException e) {
        throw new RuntimeException(e);
    } catch (IllegalAccessException e) {
        throw new RuntimeException(e);
    }
}

// 运行结果:
ClassName: Reflection.Student
name java.lang.String private
age int public
Student()
反射私有属性修改了name:liren

五、操作类的方法(例子)

官方文档

代码语言:javascript
复制
public static void main(String[] args) {
    try {
        Class<Student> cs = Student.class;

        // 获取带参的私有方法,然后设置权限,最后使用invoke()方法进行调用
        Method func_method = cs.getDeclaredMethod("function", String.class);
        func_method.setAccessible(true);
        func_method.invoke((Student)cs.newInstance(), "我是给私有的function函数传的参数");

        // 还可以输出方法的指定信息
        System.out.println("方法名:" + func_method.getName());
        System.out.println("修饰符为:" + Modifier.toString(func_method.getModifiers()));

    } catch (NoSuchMethodException e) {
        throw new RuntimeException(e);
    } catch (InvocationTargetException e) {
        throw new RuntimeException(e);
    } catch (IllegalAccessException e) {
        throw new RuntimeException(e);
    } catch (InstantiationException e) {
        throw new RuntimeException(e);
    }
}

// 运行结果:
Student()
我是给私有的function函数传的参数
方法名:function
修饰符为:private

附录

代码语言:javascript
复制
class Student {
    // 私有属性name
    private String name = "bit";
    // 公有属性age
    public int age = 18;

    // 不带参数的构造⽅法
    public Student() {
        System.out.println("Student()");
    }

    private Student(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("Student(String,name)");
    }

    private void eat() {
        System.out.println("i am eat");
    }

    void sleep() {
        System.out.println("i am pig");
    }

    private void function(String str) {
        System.out.println(str);
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Ⅰ. 概念与意义
  • Ⅱ. 反射相关的类与操作
    • 一、Class类(一切反射的起点)
    • 二、获取Class类的三种方式(Student类见附录)
    • 三、创建对象的两种方式
      • ① 直接调用 Class 中的 newInstance() 方法
      • ② 生成 Constructor 构造方法对象
    • 四、操作类的字段(例子)
    • 五、操作类的方法(例子)
  • 附录
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档