反射机制java - 深入解析java反射




什么是反思,为什么它有用? (14)

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

例如,Java中的所有对象都有getClass()方法,即使您在编译时不知道对象的类,也可以确定对象的类(例如,如果将其声明为Object ) - 这可能看起来很平凡,但这样反射在C++等动态较差的语言中是不可能的。 更高级的用途可以让你列出和调用方法,构造函数等。

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

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

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

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


Java反射使得可以在运行时检查类,接口,字段和方法,而无需在编译时知道类名,方法等。 大多数情况下,在框架层面可以实现反思的最大收益。 如果在运行时需要额外修改以用于在方法级别,实例变量级别,构造函数级别,注释级别反射中检查,修改,添加更多字节代码或者另一个程序或另一个框架,则编译的字节代码可能很有用。

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


Reflection有很多用途 。 我更熟悉的一个就是能够即时创建代码。

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


反射是一个API,用于在运行时检查或修改方法,类,接口的行为。

  1. java.lang.reflect package提供了所需的反射类。
  2. 反射为我们提供了关于对象所属的类的信息,以及可以使用该对象执行的那个类的方法。
  3. 通过反射,我们可以在运行时调用方法,而不考虑与它们一起使用的访问说明符。

java.langjava.lang.reflect包为Java反射提供了类。

反射可以用来获取有关的信息 -

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

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

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

Reflection API主要用于:

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

使用反射的优点:

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

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

缺点:

性能开销:反射操作的性能比非反射性操作的性能要差,应该在性能敏感的应用程序中频繁调用的代码段中避免。

内部曝光:反射代码打破抽象,因此可能会随着平台的升级而改变行为。

Ref: Java Reflection javarevisited.blogspot.in


从java文档page

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

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

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

它包含以下功能。

  1. 获取Class对象,
  2. 检查类(字段,方法,构造函数)的属性
  3. 设置和获取字段值,
  4. 调用方法,
  5. 创建对象的新实例。

查看这个由Class类公开的方法的documentation链接。

article (由Sosnoski Software Solutions,Inc总裁Dennis Sosnoski撰写)和article (安全探索pdf):

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

反思用户:

  1. 它提供了动态链接程序组件的非常灵活的方式
  2. 这对于创建以非常一般的方式处理对象的库很有用

反思的缺点:

  1. 用于字段和方法访问时,反射比直接代码慢得多。
  2. 它可以掩盖代码中实际发生的情况
  3. 它绕过源代码可能会造成维护问题
  4. 反射码也比相应的直接码更复杂
  5. 它允许违反关键的Java安全限制,如数据访问保护和类型安全

一般滥用情况:

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

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

我如何阅读Java中的私人领域?

概要:

在系统代码中不安全地使用其功能也很容易导致Java安全模式 l 的破坏所以请谨慎使用此功能


反射使您能够编写更通用的代码。 它允许您在运行时创建对象并在运行时调用它的方法。 因此该程序可以进行高度参数化。 它也允许反思对象和类来检测暴露于外部世界的变量和方法。


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

例如,假设你在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#)中非常相似。 在动态类型语言中,上述用例不太必要(因为编译器将允许任何方法在任何对象上被调用,如果不存在则在运行时失败),但第二种情况是寻找标记或以某种方式工作仍然很普遍。

重要的评论更新:

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


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

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

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

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


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

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);
    }

}

反思就是让对象看到他们的外表。 这个说法似乎与反思无关。 事实上,这是“自我认同”的能力。

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


我只想为所列出的所有内容添加一点。

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

我最喜欢使用的反射之一是下面的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();
}

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

许多框架和应用程序都使用它来调用服务,而无需真正了解代码。


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





terminology