java - 什么是反射, 为什么它有用?

  显示原文与译文双语对照的内容

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

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

时间:

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

例如假设你在Java中有一个未知类型的对象,如果有的话,你想在它上面调用'操作'方法。 java输入系统的静态不是真正设计的,除非对象符合一个已知的接口,而是使用反射。如果使用反射,你的代码可以查看对象并找到它,然后调用它。

因此,在 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# )的静态类型语言中非常相似。 在动态类型语言中,上面描述的用例很少需要( 因为编译器允许在任何对象上调用任何方法,如果它不存在,在运行时失败),但寻找标记或者以某种方式工作的方法的第二种情况仍然是通用的。

"反射"是一种语言,可以在运行时检查和动态调用类,方法,属性,等等 。 例如在Java中所有对象都有该方法 getClass,这就可以确定它的类即使你不知道它在编译时动态语言( 就像你将它声明为对象 ) - 这看起来可能微不足道,但是这样的皮肤并不在默认情况下有可能在更短的如 C++ 。

更高级的使用允许你列出和调用方法,构造函数等。

反射很重要,因为它允许你编写不需要在编译时进行任何操作的程序,使它们更加动态,因为它们可以在运行时绑定在一起。 可以根据已知接口编写代码,但是可以使用配置文件中的反射实例化要使用的实际类。

许多现代框架都广泛使用反射,因为这很有原因。

大多数其他现代语言都使用反射,而且像 python 这样的脚本语言可以被称为更紧密的集成,因为它与这些语言的常规编程模型更加自然。

反射的一个最常用的用法是以下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();
}

反射是允许应用程序或者框架使用可能还没有编写的代码的关键机制 !

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

另一个例子是用于XML解析 ( JAXP )的javaapi 。 其中,XML解析器提供程序是通过众所周知的系统属性为'plugged-in'的,这些属性用于通过反射构造新实例。

最后,最全面的例子是 spring,它使用反射来创建它的bean,以及大量使用代理

不是每种语言都支持反射,但是原则在支持它的语言中通常是相同的。

反射是对程序的结构进行"反映"的能力。 或者更具体的,可以看一下你拥有对象和类,以编程方式重新找到信息在它们实现方法。字段和接口上。 你也可以查看注解之类的东西。

在很多情况下都是有用的。 你希望在代码中动态地插入类的任何地方。 事先将要use,哪些对象方面表现的对象关系映射程序使用反射和迭代次数从数据库,而不能够实例化对象 knowing. Plug-in架构是另一个地方,反射是有用的。 在这些situations,如果能够动态地加载代码并确定是否出现类型实现适当的接口以用作插件的存在是重要的。

反射允许对新对象进行实例化,调用方法,并在运行时动态获取/设置类变量的操作,而无需事先了解它的实现。


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 。 如果该方法不是静态的,那么在调用时需要提供一个有效的MyObject实例而不是 null 。

反射还允许你访问类的私有成员/方法:


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 访问。

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

例如:
举一个远程应用程序给你的应用程序提供一个你使用他们的API方法获得的对象。 现在基于你可能需要执行某种计算的对象。
提供程序保证对象可以是 3类型,我们需要根据对象类型执行计算。
在对象上从provider,这样我们可能实现 3类。很显然,其中分别包含不同的逻辑对象中接收的信息可以在运行时,所以不能静态的代码来执行计算因此反射技术实例化的对象的类的你要完成用户所要求 based.

反射筛选器的英镑使用

反射通常被需要检查或者修改运行在Java虚拟机中的应用程序运行时行为的程序所使用。 这是一个相对高级的特性,应该只由精通语言基础知识的开发者使用。 考虑到这一点,反射是一种强大的技术,可以使应用程序执行操作,否则会不可能。

可扩展性功能

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

反射 缺点。

反射是强大的,但不应该不小心使用。 如果可以不使用反射进行操作,那么最好避免使用它。 通过反射访问代码时应注意以下问题。

  • 性能开销

因为反射涉及到动态解析的类型,所以不能执行某些Java虚拟机优化。 因此,反射操作的性能比它们的non-reflective相对应,并且应该在performance-sensitive应用程序频繁调用的代码段中避免。

  • 安全限制

反射需要运行时权限,在安全管理器下运行时可能不存在。 这对于必须在受限制的安全上下文中运行的代码( 比如在小程序中) 是非常重要的。

  • 内部内部的曝光

由于反射允许代码执行non-reflective代码中非法的操作,比如访问私有字段和方法,这样使用反射可能导致意外的副作用,可能导致代码不正常,可能破坏可移植性。 反射代码破坏抽象,因此可能会随着平台升级而改变行为。

根据我的理解:

反射允许程序员动态访问程序中的实体。 当程序员不知道某个类或者方法时,换句话说,在编写应用程序时,可以使用反射来动态地使用该类。

通常在类名称频繁更改的情况下使用。 如果出现这种情况,那么程序员重写应用程序并反复更改类的名称是很复杂的。

相反,通过使用反射,需要担心一个可能正在改变的类名。

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

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

例如在 C# 中,你可以在运行时加载一个程序集(. dll ),检查它,通过类导航并根据你找到的操作执行操作。 它还允许你在运行时创建一个类的实例,调用它的方法等。

在哪里可以有用? 对于具体情况来说都不是有用的。 例如你可以使用它来获取用于登录目的的类的名称,并根据配置文件中指定的内容创建事件处理程序等等。

...