什么是反思,为什么它有用?
我对Java特别感兴趣,但我认为在任何语言中原则都是一样的.
名称反射用于描述能够检查同一系统(或其自身)中的其他代码的代码.
例如,假设您在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#)中非常相似.在动态类型语言中,上述用例不太必要(因为编译器将允许在任何对象上调用任何方法,如果不存在则在运行时失败),但第二种情况是查找标记的方法或以某种方式工作仍然很常见.
评论更新:
检查系统中的代码并查看对象类型的能力不是反射,而是Type Introspection.然后,反射是通过利用内省在运行时进行修改的能力.这里的区别是必要的,因为有些语言支持内省,但不支持反思.一个这样的例子是C++
反射是一种语言在运行时检查和动态调用类,方法,属性等的能力.
例如,Java中的所有对象都有这个方法getClass()
,即使您在编译时不知道对象的类,也可以确定对象的类(例如,如果您将其声明为Object
) - 这可能看似微不足道,但这种反射是不可能的在动态较少的语言中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(); }
反思的用途
反射通常由程序使用,这些程序需要能够检查或修改在Java虚拟机中运行的应用程序的运行时行为.这是一个相对高级的功能,只有那些掌握了语言基础知识的开发人员才能使用.考虑到这一点,反射是一种强大的技术,可以使应用程序执行本来不可能的操作.
可扩展性功能
应用程序可以通过使用完全限定名称创建可扩展性对象的实例来使用外部的用户定义类.类浏览器和可视开发环境类浏览器需要能够枚举类的成员.可视化开发环境可以从利用反射中可用的类型信息中受益,以帮助开发人员编写正确的代码.调试器和测试工具调试器需要能够检查类中的私有成员.测试工具可以利用反射系统地调用类上定义的可发现的set API,以确保测试套件中的高级代码覆盖率.
反思的缺点
反思是强大的,但不应随意使用.如果可以在不使用反射的情况下执行操作,则优选避免使用它.通过反射访问代码时,应牢记以下问题.
绩效开销
由于反射涉及动态解析的类型,因此无法执行某些Java虚拟机优化.因此,反射操作的性能低于非反射操作,并且应避免在性能敏感应用程序中频繁调用的代码段中.
安全限制
Reflection需要运行时权限,在安全管理器下运行时可能不存在.对于必须在受限安全上下文中运行的代码,例如在Applet中,这是一个重要的考虑因素.
内部接触
由于反射允许代码执行在非反射代码中非法的操作,例如访问私有字段和方法,因此使用反射可能导致意外的副作用,这可能导致代码功能失常并可能破坏可移植性.反射代码打破了抽象,因此可能会通过升级平台来改变行为.
来源:Reflection API
反射是允许应用程序或框架使用甚至可能尚未编写的代码的关键机制!
以您的典型web.xml文件为例.这将包含一个servlet元素列表,其中包含嵌套的servlet-class元素.servlet容器将处理web.xml文件,并通过反射创建每个servlet类的新实例.
另一个例子是用于XML解析的Java API (JAXP).XML解析器提供程序通过众所周知的系统属性"插入",用于通过反射构造新实例.
最后,最全面的例子是Spring,它使用反射来创建它的bean,以及它对代理的大量使用
并非每种语言都支持反射,但原则在支持它的语言中通常是相同的.
反思是能够"反映"程序结构.或者更具体.查看您拥有的对象和类,并以编程方式获取有关它们实现的方法,字段和接口的信息.您还可以查看注释等内容.
它在很多情况下都很有用.您希望能够在您的代码中动态插入类.对象关系映射器的批次使用反射能够从数据库实例化对象,而无需事先知道它们将使用哪些对象.插件架构是另一个反射有用的地方.能够动态加载代码并确定是否存在实现正确接口以用作插件的类型在这些情况下非常重要.
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,但如果过度使用它可能会减慢应用程序的速度,因为它在运行时解析所有类型.
示例:
以远程应用程序为例,它为您的应用程序提供了一个使用API方法获得的对象.现在基于对象,您可能需要执行某种计算.
提供者保证对象可以是3种类型,我们需要根据对象的类型执行计算.
所以我们可以在3个类中实现,每个类包含不同的逻辑.显然,对象信息在运行时可用,因此您无法静态编码以执行计算,因此反射用于实例化您需要执行计算的类的对象.从提供者处收到的对象.
Java Reflection非常强大,非常有用.Java Reflection可以在运行时检查类,接口,字段和方法,而无需在编译时知道类,方法等的名称.还可以使用反射来实例化新对象,调用方法和获取/设置字段值.
一个快速Java反射示例,向您展示使用反射的样子:
Method[] methods = MyObject.class.getMethods(); for(Method method : methods){ System.out.println("method = " + method.getName()); }
此示例从名为MyObject的类中获取Class对象.使用类对象,该示例获取该类中方法的列表,迭代方法并打印出它们的名称.
这里解释了所有这些是如何工作的
编辑:差不多1年后,我正在编辑这个答案,因为在阅读反思时,我得到了更多反射的用法.
Spring使用bean配置,例如:
当Spring上下文处理这个
然后它将再次使用反射来获取
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);
反思的简单例子.在国际象棋游戏中,您不知道用户在运行时将移动什么.反射可用于调用已在运行时实现的方法.
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); } }
Reflection是一种API,用于在运行时检查或修改方法,类,接口的行为.
反射所需的类别在下面提供java.lang.reflect package
.
Reflection为我们提供了有关对象所属的类的信息,以及可以使用该对象执行的该类的方法.
通过反射,我们可以在运行时调用方法,而不管它们使用的访问说明符.
在java.lang
和java.lang.reflect
包Java反射提供类.
反射可用于获取有关的信息 -
Class该getClass()
方法用于获取对象所属的类的名称.
构造函数该getConstructors()
方法用于获取对象所属的类的公共构造函数.
方法该getMethods()
方法用于获取对象所属类的公共方法.
该反射API主要用于:
IDE(集成开发环境),例如Eclipse,MyEclipse,NetBeans等.
调试器和测试工具等.
使用反射的优点:
可扩展性功能:应用程序可以通过使用完全限定名称创建可扩展性对象的实例来使用外部的用户定义类.
调试和测试工具:调试器使用反射属性来检查类上的私有成员.
缺点:
性能开销:反射操作的性能低于非反射操作,应避免在性能敏感应用程序中频繁调用的代码段中.
内部曝光:反射代码打破抽象,因此可能会通过升级平台来改变行为.
参考:Java反射 javarevisited.blogspot.in
据我了解:
Reflection允许程序员动态访问程序中的实体.即,如果程序员不知道某个类或其方法,则在编写应用程序时,他可以通过使用反射动态地(在运行时)使用这样的类.
它经常用于类名经常更改的场景中.如果出现这种情况,那么程序员重写应用程序并一次又一次地更改类的名称是很复杂的.
相反,通过使用反射,需要担心可能更改的类名.
Reflection是一组函数,允许您访问程序的运行时信息并修改它的行为(有一些限制).
它很有用,因为它允许您根据程序的元信息更改运行时行为,也就是说,您可以检查函数的返回类型并更改处理情况的方式.
例如,在C#中,您可以在运行时加载程序集(.dll),检查它,浏览类并根据您找到的内容执行操作.它还允许您在运行时创建类的实例,调用其方法等.
它在哪里有用?每次都没有用,但具体情况.例如,您可以使用它来获取用于登录目的的类的名称,根据配置文件中指定的内容为dinamically创建事件的处理程序,等等......
我只想为列出的所有内容添加一些观点.
使用Reflection API,您可以toString()
为任何对象编写通用方法.
它在调试时很有用.
这是一些例子:
class ObjectAnalyzer { private ArrayList
反思是让对象看到它们的外观.这个论点似乎与反思无关.实际上,这就是"自我认同"的能力.
对于像Java和C#这样缺乏自我知识和自我感知能力的语言来说,反射本身就是一个词.因为他们没有自我知识的能力,当我们想要观察它的样子时,我们必须有另一件事来反思它的样子.优秀的动态语言(如Ruby和Python)可以在没有其他人帮助的情况下感知自己的反映.我们可以说Java的对象无法在没有镜像的情况下看到它的样子,镜像是反射类的对象,但Python中的对象可以在没有镜像的情况下感知它.这就是我们需要在Java中进行反射的原因.
来自java文档页面
java.lang.reflect
package提供了用于获取有关类和对象的反射信息的类和接口.Reflection允许以编程方式访问有关已加载类的字段,方法和构造函数的信息,以及使用反射字段,方法和构造函数在安全限制内对其基础对应项进行操作.
AccessibleObject
如果必要,可以禁止访问检查ReflectPermission
.
类此包,伴随着java.lang.Class
适应应用,如调试器,口译,目标督察,类浏览器,以及诸如服务Object Serialization
和JavaBeans
需要访问到任何一个目标对象的公共成员(基于其运行时类)或成员的声明给定的课程
它包括以下功能.
获取Class对象,
检查类的属性(字段,方法,构造函数),
设置和获取字段值,
调用方法,
创建对象的新实例.
查看本文档链接,了解Class
类公开的方法.
从这个文章(Dennis Sosnoski撰写,总裁,Sosnoski撰写软件解决方案公司)及本文章(安全探索PDF):
我可以看到使用Reflection的相当大的缺点
反思的用户:
它提供了动态链接程序组件的多种方式
它对于创建以非常通用的方式处理对象的库非常有用
反思的缺点:
当用于字段和方法访问时,反射比直接代码慢得多.
它可以掩盖代码中实际发生的事情
它绕过源代码可能会产生维护问题
反射代码也比相应的直接代码更复杂
它允许违反关键的Java安全约束,例如数据访问保护和类型安全
一般滥用:
加载限制类,
获取对受限类的构造函数,方法或字段的引用,
创建新对象实例,方法调用,获取或设置受限类的字段值.
看看有关滥用反射功能的SE问题:
如何在Java中读取私有字段?
摘要:
不安全地使用从系统代码中执行的功能也很容易导致Java安全模式 l 的危害.因此请谨慎使用此功能
正如名称本身所暗示的那样,除了提供调用在运行时动态创建实例的方法的功能之外,它还反映了它所拥有的示例类方法等.
许多框架和应用程序使用它来调用服务而不需要实际知道代码.
反射使您能够编写更多通用代码.它允许您在运行时创建对象并在运行时调用其方法.因此,程序可以高度参数化.它还允许内省对象和类来检测暴露给外部世界的变量和方法.
Reflection
有很多用途.我更熟悉的是能够动态创建代码.
IE:动态类,函数,构造函数 - 基于任何数据(xml/array/sql results/hardcoded/etc.)