当前位置: 代码网 > it编程>编程语言>Java > Java反射机制是什么?一文带你深入理解反射原理和应用

Java反射机制是什么?一文带你深入理解反射原理和应用

2026年08月16日 Java 我要评论
引言java反射是java语言中的一种动态机制,它允许在运行时检查和操作类的结构和行为。反射的强大功能使得程序可以在运行时动态加载类、调用方法和访问字段,从而极大地增强了java程序的灵活性和扩展性。

引言

java反射是java语言中的一种动态机制,它允许在运行时检查和操作类的结构和行为。

反射的强大功能使得程序可以在运行时动态加载类、调用方法和访问字段,从而极大地增强了java程序的灵活性和扩展性。

反射的基本概念

反射(reflection)是指程序在运行时能够自我检查和操作自身的能力。

通过反射,可以获取类的构造器、方法、字段等信息,并能动态调用对象的方法、设置或获取对象的字段值。

反射关键信息

  • class: 代表类的实体,在运行时加载类时会创建对应的class对象。
  • constructor: 代表类的构造方法。
  • method: 代表类的方法。
  • field: 代表类的字段。

java反射最核心的类位于jdk源码 java.lang.reflect包下,比如class、constructor、field 和 method等,他们提供了对类和对象运行时信息进行检查和操作的方法。

反射基本原理

java反射的核心在于class类,它包含了关于类的所有信息。

在java虚拟机(jvm)加载类时,会为每个类创建一个对应的class对象,该对象保存了类的元数据。

通过这些元数据,程序可以在运行时获取类的详细信息并进行操作。

主要可以从下面 4个点来阐述:

  1. 类加载:当 java程序运行时,类加载器会根据类的名称查找并加载类的字节码文件,然后将字节码文件转换为可执行的 java类,并将其存储在运行时数据区域的方法区中。
  2. 创建 class对象:在类加载过程中,java虚拟机会自动创建对应的class对象,class对象包含了类的元数据信息,并提供了访问和操作类的接口。
  3. 获取 class对象:class对象通过多种方式获取,最常见的方式有 3种: 类的 .class属性、类实例的 getclass()方法、class.forname()。
  4. 访问和操作:通过class对象获取类的字段、方法、构造函数等信息,使用field类和method类来访问和操作字段和方法,甚至可以调用私有的字段和方法。

通过上述的分析可以看出:反射机制需要基于java虚拟机对类的加载、存储和访问机制的支持,通过反射,可以在运行时动态地探索和操作类的信息,实现灵活的编程和代码的动态行为。

反射应用场景

很多优秀的框架内部都使用了java反射,这里重点讲解下给 java打下半壁江山的 spring生态(spring framework,spring mvc,springboot, springcloud…),以 spring framework为例:

  1. 依赖注入(dependency injection) : 依赖注入,可以把程序员主动创建对象的事情交给 spring管理,大大提升了对象创建的灵活性。当我们在配置文件或用注解定义 bean时,spring会使用反射来动态地实例化对象,并将依赖的其他对象注入到这些实例中。
  2. 自动装配(autowired) : 当 spring容器启动时,它会扫描应用程序中的所有类,并使用反射来查找和识别带有 @autowired注解的字段、方法或构造函数。再自动将 bean注入到需要的位置,实现对象之间的自动连接。
  3. aop(aspect-oriented programming) : aop 利用了动态代理和反射机制。通过定义切面(aspect)和切点(pointcut),spring可以在运行时使用反射来创建代理对象,从而实现横切关注点(cross-cutting concerns)的功能,如日志记录、事务管理等。
  4. 动态代理(dynamic proxy) : spring利用 java反射机制动态地创建代理对象,并在代理对象中添加额外的逻辑,从而实现对目标对象的增强。
  5. 框架扩展和定制: spring通过反射机制来实现对应用程序的扩展和定制的。例如,spring提供了beanpostprocessor接口,允许开发人员在 bean初始化前后插入自定义逻辑,这是通过反射来实现的。

另外,还有一些耳熟能详的框架也使用了java反射

  1. junit:junit是一个优秀的单元测试框架,它利用了 java反射机制动态地加载和执行测试方法。
  2. jackson:jackson是一个 json处理的 java库,它利用反射来实现 json与 java对象之间的转换,动态读取和写入 java对象的属性,并将其转换为 json格式。
  3. hibernate orm:hibernate和 mybatis一样,都是对象关系映射框架,通过反射来实现对象与数据库表之间的映射关系。

总结以下几点:

  • 框架设计: 许多java框架(如spring、hibernate)广泛使用反射来实现依赖注入、面向切面编程等功能。
  • 调试和测试: 反射允许动态访问和修改对象,方便调试和测试私有方法和字段。
  • 动态代理: 通过反射实现动态代理,增强程序的灵活性和可扩展性。
  • 类浏览器和可视化工具: 反射帮助开发工具展示类的结构和关系。

反射基本使用

获取类的class对象

class<?> clazz = class.forname("com.example.myclass");
// 或者
class<?> clazz = myclass.class;
// 或者
class<?> clazz = myobject.getclass();

获取构造方法并实例化对象

constructor<?> constructor = clazz.getconstructor(string.class);
object instance = constructor.newinstance("example");

获取和调用方法

method method = clazz.getmethod("mymethod", string.class);
method.invoke(instance, "hello");

获取和修改字段

field field = clazz.getdeclaredfield("myfield");
// 允许访问私有字段
field.setaccessible(true); 
field.set(instance, "new value");
string value = (string) field.get(instance);

反射工具类

@slf4j
public class reflectionutil {
    /**
     * 获取属性名以及对应的属性值
     *
     * @param o 对象
     * @return map
     */
    public static map<string, object> getfieldnameandvalue(object o) {

        map<string, object> resmap = new linkedhashmap<>();
        class<?> clazz = o.getclass();
        while (objects.nonnull(clazz)) {
            field[] fields = clazz.getdeclaredfields();
            for (field field : fields) {
                string fieldname = field.getname();
                if (objects.isnull(resmap.get(fieldname))) {
                    resmap.put(fieldname, getfieldvaluebyname(fieldname, o));
                }
            }
            clazz = clazz.getsuperclass();
        }
        return resmap;
    }
    /**
     * 获取属性名以及对应的属性值
     *
     * @param list 对象数组
     * @return list\<map\>
     */
    public static list<map<string, object>> getfieldnameandvaluemaps(list<?> list) {

        list<map<string, object>> resmaps = new arraylist<>(list.size());
        for (object o : list) {
            if (objects.isnull(o)) {
                throw new runtimeexception("arrays cannot contain null... ...");
            }
            map<string, object> fieldnameandvaluemap = getfieldnameandvalue(o);
            resmaps.add(fieldnameandvaluemap);
        }
        return resmaps;
    }

    public static list<string> getfieldnames(class<?> clazz) {
        list<string> fieldlist = new arraylist<>();
        while (objects.nonnull(clazz)) {
            field[] fields = clazz.getdeclaredfields();
            for (field field : fields) {
                string fieldname = field.getname();
                fieldlist.add(fieldname);
            }
            clazz = clazz.getsuperclass();
        }
        return fieldlist;
    }

    /**
     * 获取类上的指定字段。如果在类本身上找不到该字段,则将递归检查超类。
     *
     * @param clazz     source class
     * @param fieldname 字段名
     * @return 字段
     */
    private static field getfield(class<?> clazz, string fieldname) {
        try {
            return clazz.getdeclaredfield(fieldname);
        } catch (nosuchfieldexception nsf) {
            if (clazz.getsuperclass() != null) {
                return getfield(clazz.getsuperclass(), fieldname);
            }

            throw new illegalstateexception("could not locate field '" + fieldname + "' on class " + clazz);
        }
    }

    /**
     * 根据属性名获取属性值
     *
     * @param fieldname 属性名称
     * @param o         对象
     * @return object
     */
    public static object getfieldvaluebyname(string fieldname, object o) {
        try {
            string firstletter = fieldname.substring(0, 1).touppercase();
            string getter = "get" + firstletter + fieldname.substring(1);
            method method = o.getclass().getmethod(getter);
            return method.invoke(o);
        } catch (exception e) {
            log.info("根据属性名获取属性值异常:" + e.getmessage() + "\n" + e);
            return null;
        }
    }

    @suppresswarnings({"all"})
    public static void setfiledvalue(object bean, string filedname, object value) throws nosuchfieldexception, illegalaccessexception {
        class<?> aclass = bean.getclass();
        field field = getfield(aclass, filedname);
        field.setaccessible(true);
        field.set(bean, value);
    }
}

反射源码解读

反射的实现依赖于jvm提供的本地方法接口(jni),通过调用本地方法实现对类信息的获取和操作。

获取class对象的源码

public static class<?> forname(string classname) throws classnotfoundexception {
    classloader classloader = thread.currentthread().getcontextclassloader();
    if (classloader == null) {
        classloader = classloader.getsystemclassloader();
    }
    return classloader.loadclass(classname);
}

调用方法的源码

public object invoke(object obj, object... args) throws illegalaccessexception, illegalargumentexception, invocationtargetexception {
      if (!override) {
          if (!reflection.quickcheckmemberaccess(clazz, modifiers)) {
              class<?> caller = reflection.getcallerclass();
              checkaccess(caller, clazz, obj, modifiers);
          }
      }
      methodaccessor ma = methodaccessor;             // read volatile
      if (ma == null) {
          ma = acquiremethodaccessor();
      }
      return ma.invoke(obj, args);
}

从源码可以看出:method.invoke()方法,真实返回的是接口methodaccessor.invoke()方法。methodaccessor接口有三个实现类,具体是调用哪个类的 invoke 方法?

跟到源码最后可以发现:

method.invoke()方法最终调用 native的invoke0(),应用层面的操作最终转换成对操作系统 c/c++方法的调用。

反射优缺点

优点

  • 灵活性: 反射允许在运行时动态操作类,提高了程序的灵活性和扩展性。
  • 动态代理: 通过反射可以实现动态代理机制,广泛应用于aop(面向切面编程)等领域。
  • 通用性: 反射可以用来编写通用的框架和库,增强代码的重用性。

缺点

  • 性能开销: 反射操作较为耗时,可能会影响程序性能。
  • 安全问题: 反射可以绕过访问控制,修改私有字段和方法,可能引发安全问题。
  • 代码复杂性: 使用反射可能增加代码的复杂性和维护难度。

为什么需要反射

反射机制在 java中的作用不言而喻,下面列举了反射机制的一些常见场景和原因:

  1. 运行时类型检查:反射机制允许在运行时获取类的信息,包括字段、方法和构造方法等。因此,在进行运行时类型检查,以确保代码在处理不同类型的对象时能够正确地进行操作。
  2. 动态创建对象:通过反射,可以在运行时动态地创建对象,而不需要在编译时知道具体的类名。这对于某些需要根据条件或配置来创建对象的情况非常有用,例如工厂模式或依赖注入框架。
  3. 访问和修改私有成员:反射机制可以绕过访问权限限制,访问和修改类的私有字段和方法。虽然这破坏了封装性原则,但在某些特定情况下,这种能力可以帮助我们进行一些特殊操作,例如单元测试、调试或框架的内部实现。
  4. 动态调用方法:反射机制允许我们在运行时动态地调用类的方法,甚至可以根据运行时的条件来选择不同的方法。这对于实现插件化系统、处理回调函数或实现动态代理等功能非常有用。
  5. 框架和库的实现:许多java框架和库在其实现中广泛使用了反射机制。它们利用反射来自动发现和加载类、实现依赖注入、处理注解、配置文件解析和动态代理等。反射机制使得这些框架和库更加灵活和扩展。

总结

java反射是一个强大的工具,极大地增强了java语言的动态性和灵活性。然而,在使用反射时需要权衡其性能开销和安全风险。

java反射有优点也有缺点,从整体上看,java反射是以牺牲了小部分的性能换取了更好的扩展性和灵活性,牺牲小我成就大我,而且,随着现代硬件设备能力越来越强,这点小性能的牺牲是完全值得的。

理解反射的原理和使用场景,可以更好地应用反射技术来解决实际开发中的问题。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com