当前位置: 首页 > news >正文

30天学Java第十天——反射机制

反射机制

反射机制是 Java 语言中的一个重要特性,它允许程序在运行时动态地获取类的信息(如类的属性、方法和构造器等),并且可以操作这些信息。
反射机制在某些情况下非常有用,例如开发框架、库,或者需要进行动态类加载和方法调用的场景。
许多 Java 框架(如 Spring 和 Hibernate)使用反射来实现依赖注入和 AOP 切面编程。

什么是反射机制

反射机制可以理解为“运行时类型信息”,它提供了一种访问和操作对象属性与方法的能力,而无需在编译时知道对象的具体类型。通过反射,开发者可以:

  • 检查类的构造方法、方法和属性。
  • 创建对象实例。
  • 获取或设置属性的值。
  • 动态调用方法。

Java 的反射机制核心类:

反射机制的核心类在 java.lang.reflect 包中,主要包括以下几个类:

  • Class:表示类的对象,可以获取类的结构信息(属性、方法等)。
  • Field:表示类的字段,可以获取或修改该字段的值。
  • Method:表示类的方法,可以通过反射调用方法。
  • Constructor:表示类的构造器,可以通过反射创建对象实例。

如何使用反射

以下是使用 Java 反射机制的的几种应用方式:

  1. 获取 Class 对象四种方式:
  • 第一种:可以通过 Class.forName(“完全限定类名”) 获取。

    完全限定类名要带包名,用双引号括起来作为字符串
    这个方法的执行会导致类加载动作发生

    Class<?> clazz = Class.forName("com.example.MyClass");
    
  • 第二种:可以通过实例调用 getClass() 方法获取。
    注意:下面两个 clazz1 与 clazz2 是一样的,都表示 MyClass 这个类,即Java中的同一个类的类型只存在一份,反射得到的都是同一个类型
    MyClass obj1 = new MyClass();  
    MyClass obj2 = new MyClass();
    Class<?> clazz1 = obj1.getClass();
    Class<?> clazz2 = obj2.getClass();
    System.out.print(clazz1 == clazz2); // 结果是ture
    
  • 第三种:可以通过类名直接访问 ClassName.class 获取。
    Class<?> clazz = MyClass.class;
    
  • 第四种:直接通过系统 / 应用类加载器的 getSystemClassLoader() 方法获取
    这种方法的类加载不会进行初始化,只有当该类第一次使用的时候才会初始化
    ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
    Class<?> clazz = systemClassLoader .loadClass("com.example.MyClass");
    
  1. 创建对象
    MyClass obj = (MyClass) clazz.newInstance(); // 注意:此方法在 Java 9 以后被弃用,推荐使用 Constructor
    //使用 Constructor 创建对象
    Constructor<?> constructor = clazz.getConstructor();  
    MyClass obj = (MyClass) constructor.newInstance(); // 使用 Constructor 创建对象
    
  2. 获取和操作字段、方法和构造器的信息:
  • 使用getField(), getFields() ,getDeclaredField() 方法获取字段信息。

    getField(), getFields()只能获取 public 修饰的字段
    getDeclaredField() 可以获取所有权限字段

    • 获取属性名字:getName()
    • 获取属性的类型:Class getType()
      得到类型后就可以使用getName()获取类型的名字,通过getSimpleName()获取类型的简短名字
    • 获取修饰符:int getModifiers()
      返回值是 int 类型,是修饰符的 int 类型表示,可以使用 Modifier 类的 toString() 方法将 int 转换为对于的修饰符
  • 使用 Field 类的 set() 和 get() 方法操作字段的值。
    Field field = clazz.getDeclaredField("fieldName"); // 获取字段  
    field.setAccessible(true); // 如果是私有字段,需要设置为可访问  
    field.set(obj, "newValue"); // 设置字段值  
    
    String value = (String) field.get(obj); // 获取字段值
    
  • 使用 getMethod(), getDeclaredMethod() 方法获取方法信息。使用 Method 类的 invoke() 方法动态调用方法。
    Method method = clazz.getDeclaredMethod("methodName", String.class); // 根据方法名和参数类型获取方法  
    method.setAccessible(true); // 如果是私有方法,需要设置为可访问  
    method.invoke(obj, "argument"); // 调用方法
    
  • 使用 getConstructor(), getDeclaredConstructor() 方法获取构造器信息。
  1. 反射获取父类泛型
    通过 getGenericSuperclass() 方法获取当前类的父类泛型,返回值是类型 ParameterizedType(参数化类型) ,属于 Type(这是java.lang.reflect下的接口,表示一种类型)。
    然后通过 ParameterizedType.getActualTyoeArguments() 方法获取到父类泛型的类型集合
    在这里插入图片描述
  2. 获取接口、属性、方法参数、方法返回值、构造方法参数的泛型与获取父类泛型类似
    • 接口:getGenericInterfaces()
    • 属性:先获取属性,然后通过 getGenericType() 方法获取属性的泛型类型
    • 方法参数:先获取方法,然后通过 getGenericParameterTypes() 方法获取方法参数的泛型类型
    • 方法返回值:先获取方法,然后通过 getGenericReturnType() 方法获方法返回值的泛型类型
    • 构造方法参数:先获取构造方法,然后通过 getGenericParameterTypes() 方法获取构造方法参数的泛型类型

反射的缺点

尽管反射机制提供了强大的灵活性,但它也有一些缺点:

  • 性能开销: 反射操作通常比直接代码调用慢,因为它涉及到多层间接调用和更复杂的安全检查。
  • 安全性问题: 反射绕过了一些访问控制检查,可能导致安全隐患。
  • 代码可读性: 使用反射会使代码变得不太清晰和可读,尤其是当涉及复杂的反射逻辑时。

反射的示例

import java.lang.reflect.Constructor;  
import java.lang.reflect.Field;  
import java.lang.reflect.Method;  

class Person {  
    private String name;  

    public Person() { }  

    public void setName(String name) {  
        this.name = name;  
    }  

    public String getName() {  
        return name;  
    }  
}  

public class ReflectionExample {  
    public static void main(String[] args) {  
        try {  
            // 1. 获取 Class 对象  
            Class<?> clazz = Class.forName("Person");  

            // 2. 创建实例  
            Constructor<?> constructor = clazz.getConstructor();  
            Object personInstance = constructor.newInstance();  

            // 3. 调用方法设置属性  
            Method setNameMethod = clazz.getDeclaredMethod("setName", String.class);  
            setNameMethod.invoke(personInstance, "John Doe");  

            // 4. 访问字段  
            Field nameField = clazz.getDeclaredField("name");  
            nameField.setAccessible(true); // 访问私有字段  
            String nameValue = (String) nameField.get(personInstance);  
            
            // 5. 输出结果  
            System.out.println("Name: " + nameValue); // 输出: Name: John Doe  

        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }  
}

注:本文章源于学习动力节点老杜的java教程视频后的笔记整理,方便自己复习的同时,也希望能给csdn的朋友们提供一点帮助。

相关文章:

  • 游戏引擎学习第225天
  • visual studio 常用的快捷键(已经熟悉的就不记录了)
  • 《轨道力学讲义》——第八讲:行星际轨道设计
  • 项目执行中的目标管理:从战略到落地的闭环实践
  • 远程开发之devcontainer[简单介绍版]
  • Kitex Release v0.13.0正式发布!
  • linux 系统编程基础部分 day1
  • Innovus常见 ERROR: (IMPOPT-628)全自动解决方案(ecoChangeCell报错问题)
  • 智算网络新标杆:全栈AI方案如何实现无损带宽与多租户隔离?
  • 【学习】Codeforces Round 861 (Div. 2) C. Unlucky Numbers
  • leecode Hot100之回溯算法【C++速查】
  • 【Linux】进程的详讲(下)--进程的环境变量
  • 高边MOSFET(High-Side MOSFET)
  • Python Path对象symlink_to方法介绍
  • 文本纠错WPS插件:提升文档质量的利器
  • 关于 CSDN的C知道功能模块 的详细解析,包括 新增的AI搜索(可选深度思考) 和 智能体功能 的具体说明及对比分析
  • 实验三 多表查询和子查询
  • js的es6模块中 暴露的使用方法简介
  • 鸿蒙开发01
  • 三周年创作纪念日
  • 夜读丨秦腔里的乡魂
  • 开发国内首个泌尿专科智能体,医生们将临床经验转变为知识图谱
  • 受贿超8.22亿元,新疆维吾尔自治区党委原副书记李鹏新一审被判死缓
  • 日媒:日本公明党党首将访华,并携带石破茂亲笔信
  • 五一出游火爆!热门线路抢票难度堪比春运,有热门目的地酒店价格涨近4倍
  • 专家学者视角下的乡村教育:目标与出路并非“走出大山”