首页 文章

什么是反思,为什么它有用?

提问于
浏览
1779

什么是反思,为什么它有用?

我对Java特别感兴趣,但我认为在任何语言中原则都是一样的 .

21 回答

  • 8

    我最喜欢使用的反射之一是下面的Java转储方法 . 它将任何对象作为参数,并使用Java反射API打印出每个字段名称和值 .

    import java.lang.reflect.Array;
    import java.lang.reflect.Field;
    
    public static String dump(Object o, int callCount) {
        callCount++;
        StringBuffer tabs = new StringBuffer();
        for (int k = 0; k < callCount; k++) {
            tabs.append("\t");
        }
        StringBuffer buffer = new StringBuffer();
        Class oClass = o.getClass();
        if (oClass.isArray()) {
            buffer.append("\n");
            buffer.append(tabs.toString());
            buffer.append("[");
            for (int i = 0; i < Array.getLength(o); i++) {
                if (i < 0)
                    buffer.append(",");
                Object value = Array.get(o, i);
                if (value.getClass().isPrimitive() ||
                        value.getClass() == java.lang.Long.class ||
                        value.getClass() == java.lang.String.class ||
                        value.getClass() == java.lang.Integer.class ||
                        value.getClass() == java.lang.Boolean.class
                        ) {
                    buffer.append(value);
                } else {
                    buffer.append(dump(value, callCount));
                }
            }
            buffer.append(tabs.toString());
            buffer.append("]\n");
        } else {
            buffer.append("\n");
            buffer.append(tabs.toString());
            buffer.append("{\n");
            while (oClass != null) {
                Field[] fields = oClass.getDeclaredFields();
                for (int i = 0; i < fields.length; i++) {
                    buffer.append(tabs.toString());
                    fields[i].setAccessible(true);
                    buffer.append(fields[i].getName());
                    buffer.append("=");
                    try {
                        Object value = fields[i].get(o);
                        if (value != null) {
                            if (value.getClass().isPrimitive() ||
                                    value.getClass() == java.lang.Long.class ||
                                    value.getClass() == java.lang.String.class ||
                                    value.getClass() == java.lang.Integer.class ||
                                    value.getClass() == java.lang.Boolean.class
                                    ) {
                                buffer.append(value);
                            } else {
                                buffer.append(dump(value, callCount));
                            }
                        }
                    } catch (IllegalAccessException e) {
                        buffer.append(e.getMessage());
                    }
                    buffer.append("\n");
                }
                oClass = oClass.getSuperclass();
            }
            buffer.append(tabs.toString());
            buffer.append("}\n");
        }
        return buffer.toString();
    }
    
  • 14

    Uses of Reflection

    反射通常由程序使用,这些程序需要能够检查或修改在Java虚拟机中运行的应用程序的运行时行为 . 这是一个相对高级的功能,只能由掌握语言基础知识的开发人员使用 . 考虑到这一点,反射是一种强大的技术,可以使应用程序执行本来不可能的操作 .

    Extensibility Features

    应用程序可以通过使用完全限定名称创建可扩展性对象的实例来使用外部的用户定义类 . 类浏览器和可视开发环境类浏览器需要能够枚举类的成员 . 可视化开发环境可以从利用反射中可用的类型信息中受益,以帮助开发人员编写正确的代码 . 调试器和测试工具调试器需要能够检查类中的私有成员 . 测试工具可以利用反射系统地调用类上定义的可发现的set API,以确保测试套件中的高级代码覆盖率 .

    Drawbacks of Reflection

    反思是强大的,但不应滥用 . 如果可以在不使用反射的情况下执行操作,则优选避免使用它 . 通过反射访问代码时,应牢记以下问题 .

    • Performance Overhead

    由于反射涉及动态解析的类型,因此无法执行某些Java虚拟机优化 . 因此,反射操作比非反射操作具有更慢的性能,并且应该在性能敏感应用中频繁调用的代码段中避免 .

    • Security Restrictions

    Reflection需要运行时权限,在安全管理器下运行时可能不存在 . 对于必须在受限安全上下文中运行的代码,例如在Applet中,这是一个重要的考虑因素 .

    • Exposure of Internals

    由于反射允许代码执行在非反射代码中非法的操作,例如访问私有字段和方法,因此使用反射可能导致意外的副作用,这可能导致代码功能失常并可能破坏可移植性 . 反射代码打破了抽象,因此可能会通过升级平台来改变行为 .

    来源:The Reflection API

  • 20

    来自java文档page

    java.lang.reflect包提供了用于获取有关类和对象的反射信息的类和接口 . Reflection允许以编程方式访问有关已加载类的字段,方法和构造函数的信息,以及使用反射字段,方法和构造函数在安全限制内对其基础对应项进行操作 .

    AccessibleObject 允许在必要的 ReflectPermission 可用时禁止访问检查 .

    此包中的类以及 java.lang.Class 适用于调试器,解释器,对象检查器,类浏览器以及需要访问目标对象的公共成员(基于其运行时类)或 Object SerializationJavaBeans 等服务的应用程序 . 由给定类声明的成员

    它包括以下功能 .

    • 获取Class对象,

    • 检查类的属性(字段,方法,构造函数),

    • 设置和获取字段值,

    • 调用方法,

    • 创建对象的新实例 .

    看看这个documentation链接,了解 Class 类公开的方法 .

    来自article(Sosnoski软件解决方案公司总裁Dennis Sosnoski)和article(安全探索pdf):

    我可以看到比使用Reflection更多的缺点

    User of Reflection:

    • 它提供了动态链接程序组件的多种方式

    • 创建以非常通用的方式处理对象的库非常有用

    Drawbacks of Reflection:

    • 当用于字段和方法访问时,反射比直接代码慢得多 .

    • 它可以掩盖代码中实际发生的事情

    • 它绕过源代码可能会产生维护问题

    • 反射代码也比相应的直接代码更复杂

    • 它允许违反关键Java安全约束,例如数据访问保护和类型安全

    General abuses:

    • 加载限制类,

    • 获取对受限类的构造函数,方法或字段的引用,

    • 创建新对象实例,方法调用,获取或设置限制类的字段值 .

    看看有关滥用反射功能的SE问题:

    How do I read a private field in Java?

    Summary:

    不安全地使用从系统代码中执行的功能也很容易导致Java安全模型的危害 . So use this feature sparingly

  • 8

    名称反射用于描述能够检查同一系统(或其自身)中的其他代码的代码 .

    例如,假设您在Java中有一个未知类型的对象,并且您希望在其上调用“doSomething”方法(如果存在) . Java的静态类型系统并不是真的设计为支持这个,除非对象符合已知的接口,但是使用反射,你的代码可以查看对象并找出它是否有一个名为'doSomething'的方法然后如果你调用它想要 .

    所以,在Java中给你一个代码示例(假设有问题的对象是foo):

    Method method = foo.getClass().getMethod("doSomething", null);
    method.invoke(foo, null);
    

    Java中一个非常常见的用例是带注释的用法 . 例如,JUnit 4将使用反射来查看使用@Test注释标记的方法的类,然后在运行单元测试时调用它们 .

    有一些很好的反思例子可以帮助你开始http://docs.oracle.com/javase/tutorial/reflect/index.html

    最后,是的,这些概念在支持反射的其他静态类型语言(如C#)中非常相似 . 在动态类型语言中,上述用例不太必要(因为编译器将允许在任何对象上调用任何方法,如果不存在则在运行时失败),但第二种情况是查找标记的方法或以某种方式工作仍然很常见 .

    Update from a comment:

    检查系统中的代码并查看对象类型的能力不是反射,而是Type Introspection . 然后反思是通过利用内省在运行时进行修改的能力 . 这里的区别是必要的,因为有些语言支持内省,但不支持反思 . 一个这样的例子是C.

  • 205

    Reflection 是一个API,用于在运行时检查或修改方法,类和接口的行为 .

    • 反射所需的类在 java.lang.reflect package 下提供 .

    • Reflection为我们提供了有关对象所属的类的信息,以及可以使用该对象执行的该类的方法 .

    • 通过反射,我们可以在运行时调用方法,而不管它们使用的访问说明符 .

    java.langjava.lang.reflect 包提供了java反射的类 .

    Reflection 可用于获取有关的信息 -

    • Class getClass() 方法用于获取对象所属的类的名称 .

    • Constructors getConstructors() 方法用于获取对象所属的类的公共构造函数 .

    • Methods getMethods() 方法用于获取对象所属类的公共方法 .

    Reflection API 主要用于:

    IDE(集成开发环境),例如Eclipse,MyEclipse,NetBeans等
    调试器和测试工具等

    Advantages of Using Reflection:

    可扩展性功能:应用程序可以通过使用其完全限定名称创建可扩展性对象的实例来使用外部用户定义的类 .

    调试和测试工具:调试器使用反射属性来检查类上的私有成员 .

    Drawbacks:

    性能开销:反射操作的性能低于非反射操作,应避免在对性能敏感的应用程序中频繁调用的代码段中 .

    内部曝光:反射代码打破抽象,因此可能会通过升级平台来改变行为 .

    参考:Java Reflection javarevisited.blogspot.in

  • -6

    Reflection是一种语言在运行时检查和动态调用类,方法,属性等的能力 .

    例如,Java中的所有对象都有方法 getClass() ,它允许您在编译时确定对象's class even if you don't知道它(例如,如果您将其声明为 Object ) - 这可能看似微不足道,但这种反射在动态性较差时是不可能的语言如 C++ . 更高级的用法允许您列出和调用方法,构造函数等 .

    反射非常重要,因为它允许您编写不必在编译时“了解”所有内容的程序,使它们更具动态性,因为它们可以在运行时绑定在一起 . 代码可以针对已知接口编写,但是可以使用配置文件中的反射来实例化要使用的实际类 .

    由于这个原因,许多现代框架广泛使用反射 . 大多数其他现代语言也使用反射,并且在脚本语言(例如Python)中,它们甚至更紧密地集成,因为在这些语言的通用编程模型中感觉更自然 .

  • 30

    示例:
    以远程应用程序为例,它为您的应用程序提供了一个使用API方法获得的对象 . 现在基于您可能需要执行某种计算的对象 .
    提供者保证对象可以是3种类型,我们需要根据对象的类型执行计算 .
    因此,我们可以在3个类中实现,每个类包含不同的逻辑 . 显然,对象信息在运行时可用,因此您无法静态编码以执行计算,因此反射用于实例化基于此类所需执行计算的类的对象 . 从提供者处收到的对象 .

  • 19

    我想通过例子回答这个问题 . 首先, Hibernate 项目使用 Reflection API 生成 CRUD 语句,以弥合正在运行的应用程序和持久性存储之间的鸿沟 . 当域中的内容发生变化时, Hibernate 必须知道它们以将它们持久存储到数据存储中,反之亦然 .

    或者工作 Lombok Project . 它只是在编译时注入代码,导致代码被插入到您的域类中 . (我认为这对吸气者和制定者来说是可以的)

    Hibernate 选择了 reflection ,因为它对应用程序的构建过程影响很小 .

    从Java 7我们有 MethodHandles ,它的作用是 Reflection API . 在项目中,要使用 Logger ,我们只需复制粘贴下一个代码:

    Logger LOGGER = Logger.getLogger(MethodHandles.lookup().lookupClass().getName());
    

    因为在这种情况下很难犯错字错误 .

  • 93

    反射是允许应用程序或框架使用甚至可能尚未编写的代码的关键机制!

    以您的典型web.xml文件为例 . 这将包含一个servlet元素列表,其中包含嵌套的servlet-class元素 . servlet容器将处理web.xml文件,并通过反射创建每个servlet类的新实例 .

    另一个例子是XML API for XML Parsing (JAXP) . XML解析器提供程序通过众所周知的系统属性'plugged-in',用于通过反射构造新实例 .

    最后,最全面的例子是Spring,它使用反射来创建它的bean,并且使用代理的大量使用

  • 13

    Reflection是一组函数,允许您访问程序的运行时信息并修改它的行为(有一些限制) .

    它很有用,因为它允许您根据程序的元信息更改运行时行为,也就是说,您可以检查函数的返回类型并更改处理情况的方式 .

    例如,在C#中,您可以在运行时加载程序集(.dll),检查它,浏览类并根据您找到的内容执行操作 . 它还允许您在运行时创建类的实例,调用其方法等 .

    它在哪里有用?每次都没有用,但具体情况 . 例如,您可以使用它来获取用于登录目的的类的名称,根据配置文件中指定的内容为dinamically创建事件的处理程序,等等......

  • 5

    反射使您能够编写更多通用代码 . 它允许您在运行时创建对象并在运行时调用其方法 . 因此,程序可以高度参数化 . 它还允许内省对象和类来检测其暴露给外部世界的变量和方法 .

  • 8

    Java Reflection可以在运行时检查类,接口,字段和方法,而无需在编译时知道类,方法等的名称 . 大多数在框架层面,可以实现反射的最大好处 . 如果需要在运行时进行额外修改以进行检查,修改,在其自身内添加更多字节代码,或在方法级别,实例变量级别,构造函数级别,注释级别反射中的其他程序或其他框架,则编译的字节代码可能是有用的 .

    假设你有一个方法 add(Int a,int b) . 等效的字节代码是 B1 . 如果您的系统中有1000个名为 add 的方法 . 现在,您要在调用方法 add 之前检查参数 ab 的值 . 因此,您可以将代码粘贴到另一个使用反射的程序或框架,以使用 Object.getClass.getMethod() 动态检查字节代码值 . 有几个类用于检查 . 它可以在调用方法 add 之前添加更多操作 . 但是,程序本身或其他程序或框架不知道具有名为 add 的方法的对象 . 主要在依赖注入中,主要使用面向方面的编程使用反射 .

  • 4

    并非每种语言都支持反射,但支持它的语言中的原则通常是相同的 .

    反思是能够“反映”程序结构 . 或者更具体 . 查看您拥有的对象和类,并以编程方式获取有关它们实现的方法,字段和接口的信息 . 您还可以查看注释等内容 .

    它在很多情况下都很有用 . 到处都是你想要的能够动态地将类插入到您的代码中 . 对象关系映射器的批次使用反射能够从数据库中实例化对象,而无需事先知道它们将使用哪些对象 . 插件架构是另一个反射有用的地方 . 能够动态加载代码并确定是否存在实现正确接口以用作插件的类型在这些情况下非常重要 .

  • 13

    据我了解:

    Reflection允许程序员动态访问程序中的实体 . 即,如果程序员不知道某个类或其方法,则在编写应用程序时,他可以通过使用反射动态地(在运行时)使用这样的类 .

    它经常用于类名经常更改的场景中 . 如果出现这种情况,那么程序员重写应用程序并一次又一次地更改类的名称是很复杂的 .

    相反,通过使用反射,需要担心可能更改的类名 .

  • 35

    Java Reflection非常强大,非常有用 . Java Reflection使得在编译时不知道类,方法等的名称就可以实现 to inspect classes, interfaces, fields and methods at runtime, . 也有可能 instantiate new objects, invoke methods and get/set field values using reflection.

    A quick Java Reflection example to show you what using reflection looks like:

    Method[] methods = MyObject.class.getMethods();
    
        for(Method method : methods){
            System.out.println("method = " + method.getName());
        }
    

    此示例从名为MyObject的类中获取Class对象 . 使用类对象,该示例获取该类中方法的列表,迭代方法并打印出它们的名称 .

    Exactly how all this works is explained here

    Edit :差不多1年后,我正在编辑这个答案,因为在阅读反思的过程中,我得到了更多反射的用法 .

    • Spring使用bean配置,例如:
    <bean id="someID" class="com.example.Foo">
        <property name="someField" value="someValue" />
    </bean>
    

    当Spring上下文处理这个<bean>元素时,它将使用Class.forName(String)和参数“com.example.Foo”来实例化该Class .

    然后它将再次使用反射来获取<property>元素的相应setter并将其值设置为指定值 .

    • Junit特别使用Reflection来测试私有/受保护的方法 .

    对于私人方法,

    Method method = targetClass.getDeclaredMethod(methodName, argClasses);
    method.setAccessible(true);
    return method.invoke(targetObject, argObjects);
    

    对于私人领域,

    Field field = targetClass.getDeclaredField(fieldName);
    field.setAccessible(true);
    field.set(object, value);
    
  • 32

    Reflection允许在运行时动态地实例化新对象,调用方法和对类变量进行get / set操作,而无需事先了解其实现 .

    Class myObjectClass = MyObject.class;
    Method[] method = myObjectClass.getMethods();
    
    //Here the method takes a string parameter if there is no param, put null.
    Method method = aClass.getMethod("method_name", String.class); 
    
    Object returnValue = method.invoke(null, "parameter-value1");
    

    在上面的示例中,null参数是您要在其上调用方法的对象 . 如果方法是静态的,则提供null . 如果方法不是静态的,那么在调用时需要提供有效的MyObject实例而不是null .

    Reflection还允许您访问类的私有成员/方法:

    public class A{
    
      private String str= null;
    
      public A(String str) {
      this.str= str;
      }
    }
    

    .

    A obj= new A("Some value");
    
    Field privateStringField = A.class.getDeclaredField("privateString");
    
    //Turn off access check for this field
    privateStringField.setAccessible(true);
    
    String fieldValue = (String) privateStringField.get(obj);
    System.out.println("fieldValue = " + fieldValue);
    
    • 对于类的检查(也称为内省),您不需要导入反射包( java.lang.reflect ) . 可以通过 java.lang.Class 访问类元数据 .

    Reflection是一个非常强大的API,但如果过度使用它可能会降低应用程序的速度,因为它在运行时解析了所有类型 .

  • 0

    反思的简单例子 . 在国际象棋游戏中,您不知道用户在运行时将移动什么 . 反射可用于调用已在运行时实现的方法 .

    public class Test {
    
        public void firstMoveChoice(){
            System.out.println("First Move");
        } 
        public void secondMOveChoice(){
            System.out.println("Second Move");
        }
        public void thirdMoveChoice(){
            System.out.println("Third Move");
        }
    
        public static void main(String[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { 
            Test test = new Test();
            Method[] method = test.getClass().getMethods();
            //firstMoveChoice
            method[0].invoke(test, null);
            //secondMoveChoice
            method[1].invoke(test, null);
            //thirdMoveChoice
            method[2].invoke(test, null);
        }
    
    }
    
  • 1471

    我只是想为列出的所有内容添加一些观点 .

    使用 Reflection API ,您可以为任何对象编写通用 toString() 方法 .

    它在调试时很有用 .

    这是一些例子:

    class ObjectAnalyzer {
    
       private ArrayList<Object> visited = new ArrayList<Object>();
    
       /**
        * Converts an object to a string representation that lists all fields.
        * @param obj an object
        * @return a string with the object's class name and all field names and
        * values
        */
       public String toString(Object obj) {
          if (obj == null) return "null";
          if (visited.contains(obj)) return "...";
          visited.add(obj);
          Class cl = obj.getClass();
          if (cl == String.class) return (String) obj;
          if (cl.isArray()) {
             String r = cl.getComponentType() + "[]{";
             for (int i = 0; i < Array.getLength(obj); i++) {
                if (i > 0) r += ",";
                Object val = Array.get(obj, i);
                if (cl.getComponentType().isPrimitive()) r += val;
                else r += toString(val);
             }
             return r + "}";
          }
    
          String r = cl.getName();
          // inspect the fields of this class and all superclasses
          do {
             r += "[";
             Field[] fields = cl.getDeclaredFields();
             AccessibleObject.setAccessible(fields, true);
             // get the names and values of all fields
             for (Field f : fields) {
                if (!Modifier.isStatic(f.getModifiers())) {
                   if (!r.endsWith("[")) r += ",";
                   r += f.getName() + "=";
                   try {
                      Class t = f.getType();
                      Object val = f.get(obj);
                      if (t.isPrimitive()) r += val;
                      else r += toString(val);
                   } catch (Exception e) {
                      e.printStackTrace();
                   }
                }
             }
             r += "]";
             cl = cl.getSuperclass();
          } while (cl != null);
    
          return r;
       }    
    }
    
  • 7

    正如名称本身所暗示的那样,除了提供调用在运行时动态创建实例的方法的功能之外,它还反映了它所拥有的示例类方法等 .

    许多框架和应用程序使用它来调用服务而不需要实际知道代码 .

  • 13

    反思是让对象看到它们的外观 . 这个论点似乎与反思无关 . 实际上,这就是“自我认同”的能力 .

    对于像Java和C#这样缺乏自我知识和自我感知能力的语言来说,反思本身就是一个词 . 因为他们没有自我知识的能力,当我们想要观察它的样子时,我们必须有另一件事来反思它的样子 . 优秀的动态语言(如Ruby和Python)可以在没有其他人帮助的情况下感知自己的反映 . 我们可以说Java的对象无法在没有镜像的情况下看到它的样子,镜像是反射类的对象,但是Python中的对象可以在没有镜像的情况下感知它 . 这就是我们需要在Java中进行反射的原因 .

  • 60

    Reflection 有很多 uses . 我更熟悉的是能够动态创建代码 .

    IE:动态类,函数,构造函数 - 基于任何数据(xml / array / sql results / hardcoded / etc.)

相关问题