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

我对Java特别感兴趣,但我认为任何语言的原理都是一样的。


当前回答

从java文档页面

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

如果必要的ReflectPermission可用,AccessibleObject允许禁止访问检查。

此包中的类与java.lang.Class一起容纳调试器、解释器、对象检查器、类浏览器等应用程序,以及需要访问目标对象的公共成员(基于其运行时类)或给定类声明的成员的服务(如object Serialization和JavaBeans)

它包括以下功能。

获取类对象,检查类(字段、方法、构造函数)的财产,设置和获取字段值,调用方法,创建对象的新实例。

查看Class类公开的方法的文档链接。

本文(由Sosnoski Software Solutions,Inc总裁Dennis Sosnosiki撰写)和本文(security-explorations pdf):

与使用反射相比,我可以看到相当多的缺点

反射用户:

它提供了动态链接程序组件的多种方式它对于创建以非常通用的方式处理对象的库非常有用

反射的缺点:

当用于字段和方法访问时,反射比直接代码慢得多。它可以隐藏代码中的实际情况它绕过源代码会产生维护问题反射代码也比相应的直接代码更复杂它允许违反关键Java安全约束,例如作为数据访问保护和类型安全

一般滥用:

加载限制类,获取对受限类的构造函数、方法或字段的引用,创建新的对象实例、方法调用、获取或设置受限类的字段值。

看看这个关于滥用反射功能的SE问题:

如何在Java中读取私有字段?

摘要:

在系统代码中不安全地使用其功能也很容易导致Java安全模型的破坏。因此,谨慎使用此功能

其他回答

反射是一种语言在运行时检查和动态调用类、方法、属性等的能力。

例如,Java中的所有对象都有getClass()方法,该方法允许您确定对象的类,即使您在编译时不知道它(例如,如果您将其声明为object)-这可能看起来微不足道,但这种反射在动态性较差的语言(如C++)中是不可能的。更高级的用法允许您列出和调用方法、构造函数等。

反射是很重要的,因为它让你编写的程序不必在编译时“知道”一切,使它们更加动态,因为它们可以在运行时绑定在一起。代码可以针对已知的接口编写,但要使用的实际类可以使用配置文件的反射来实例化。

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

例子:

以一个远程应用程序为例,它为您的应用程序提供了一个对象,您可以使用其API方法获取该对象。现在,基于对象,您可能需要执行某种计算。

提供者保证对象可以是3种类型,我们需要根据对象的类型进行计算。

因此,我们可以在3个类中实现,每个类包含不同的逻辑。显然,对象信息在运行时可用,因此您无法静态编码以执行计算,因此反射用于实例化类的对象,您需要根据从提供程序接收的对象执行计算。

我只是想对所有列出的内容补充几点。

使用反射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;
   }    
}

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

许多框架和应用程序都使用它来调用服务,而实际上并不知道代码。

反射的使用

反射通常由需要检查或修改在Java虚拟机中运行的应用程序的运行时行为的程序使用。这是一个相对高级的特性,只有对语言基础有很强理解的开发人员才能使用。考虑到这一点,反射是一种强大的技术,可以使应用程序执行不可能执行的操作。

可扩展性功能

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

反射的缺点

反射是强大的,但不应滥用。如果可以在不使用反射的情况下执行一个操作,那么最好避免使用它。

性能开销

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

安全性限制

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

内部构件暴露

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

来源:反射API