什么是反思,为什么它有用?
什么是反思,为什么它有用?
我对Java特别感兴趣,但我认为任何语言的原则都是一样的。
reflection名称用于描述能够检查同一系统(或其本身)中的其他代码的代码。
例如,假设你在Java中有一个未知types的对象,并且你想调用一个“doSomething”方法(如果存在的话)。 Java的静态types系统不是真的被devise来支持这个,除非对象符合一个已知的接口,但是使用reflection,你的代码可以查看对象,并找出它是否有一个叫做“doSomething”的方法,然后调用它,如果你想要。
所以,给你一个Java代码示例(想象中的对象是foo):
Method method = foo.getClass().getMethod("doSomething", null); method.invoke(foo, null);
Java中一个非常常见的用例是带注释的用法。 例如,JUnit 4将使用reflection来查看你的类中用@Test标注标记的方法,然后在运行unit testing时调用它们。
http://docs.oracle.com/javase/tutorial/reflect/index.html有一些很好的反思示例;
最后,是的,在支持reflection的其他静态types语言(如C#)中,概念非常相似。 在dynamictypes语言中,上面描述的用例是不太必要的(因为编译器将允许任何方法在任何对象上被调用,如果它不存在则在运行时失败),而第二种情况是寻找被标记的方法以某种方式工作仍然很普遍。
从评论重要更新:
能够检查系统中的代码并查看对象types不是reflection,而是types自相关。 那么反思就是通过利用内省在运行时进行修改的能力。 这里的区分是必要的,因为有些语言支持自省,但不支持反思。 一个这样的例子是C ++
reflection是语言在运行时检查和dynamic调用类,方法,属性等的能力。
例如,Java中的所有对象都有getClass()
方法,即使您在编译时不知道对象的类,也可以确定该对象的类(例如,如果将其声明为Object
) – 这可能看起来很平常,但是reflection是不可能的dynamic语言,如C++
。 更高级的用途可以让你列出和调用方法,构造函数等。
reflection非常重要,因为它可以让你编写不需要在编译时“知道”所有东西的程序,使它们更加dynamic,因为它们可以在运行时绑定在一起。 代码可以用已知的接口进行编写,但实际使用的类可以使用来自configuration文件的reflection来实例化。
很多现代的框架因为这个原因而广泛地使用reflection。 大多数其他现代语言也使用reflection,而在脚本语言(如Python)中,它们甚至更紧密地集成在一起,因为在这些语言的通用编程模型中感觉更自然。
我最喜欢使用的reflection之一是下面的Java转储方法。 它将任何对象作为参数,并使用JavareflectionAPI打印出每个字段的名称和值。
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(); }
reflection的用途
reflection通常被需要能够检查或修改Java虚拟机中运行的应用程序的运行时行为的程序所使用。 这是一个比较先进的function,只能由对这个语言的基础知识掌握得很好的开发者来使用。 有了这个警告,reflection是一个强大的技术,可以使应用程序执行操作,否则是不可能的。
可扩展性function
应用程序可以使用外部的用户定义的类,通过使用完全限定的名称创build可扩展性对象的实例。 类浏览器和可视化开发环境类浏览器需要能够枚举类的成员。 可视化开发环境可以利用reflection中可用的types信息来帮助开发人员编写正确的代码。 debugging器和testing工具debugging器需要能够检查类上的私有成员。 testing工具可以利用reflection系统地调用一个类中定义的可发现的API,以确保testing套件中的代码覆盖率高。
反思的缺点
反思是强大的,但不应该滥用。 如果可以在不使用reflection的情况下执行操作,则最好避免使用它。 通过reflection访问代码时应注意以下几点。
- 性能开销
由于reflection涉及dynamicparsing的types,因此无法执行某些Java虚拟机优化。 因此,reflection操作的性能要比非reflection操作的性能要差,应该避免在性能敏感的应用程序中频繁调用的代码部分。
- 安全限制
reflection需要在安全pipe理器下运行时可能不存在的运行时权限。 对于必须在有限的安全上下文中运行的代码,比如在Applet中,这是一个重要的考虑因素。
- 内部曝光
由于reflection允许代码执行在非reflection代码中非法的操作,例如访问私有字段和方法,所以reflection的使用可能会导致意想不到的副作用,这可能导致代码function失常并可能破坏可移植性。 reflection代码打破抽象,因此可能会改变平台升级的行为。
来源: reflectionAPI
reflection是允许应用程序或框架使用可能还没有写入的代码的关键机制!
以典型的web.xml文件为例。 这将包含一个包含嵌套的servlet-class元素的servlet元素列表。 servlet容器将处理web.xml文件,并通过reflection创build新的每个servlet类的新实例。
另一个例子是用于XMLparsing的Java API (JAXP) 。 XMLparsing器提供者是通过众所周知的系统属性“插入”的,通过reflection来构造新的实例。
最后,最全面的例子是使用reflection来创buildbean的Spring ,以及代理的大量使用
并不是所有的语言都支持反思,但是支持语言的原则通常是相同的。
反思是能够“反映”你的计划的结构。 或者更具体一些。 要查看您拥有的对象和类,并以编程方式获取有关它们实现的方法,字段和接口的信息。 您也可以查看注释等内容。
这在很多情况下都是有用的。 到处想要能够dynamic地将类插入到代码中。 Lot的对象关系映射器使用reflection能够实例化来自数据库的对象,而无需事先知道他们将要使用的对象。 插件体系结构是reflection有用的另一个地方。 能够dynamic加载代码并确定是否有types,那里实现正确的接口作为插件使用是非常重要的在这些情况下。
reflection允许实例化新对象,调用方法以及在运行时dynamic地对类variables进行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);
- 为了检查类(也称为自省),你不需要导入reflection包(
java.lang.reflect
)。 类元数据可以通过java.lang.Class
访问。
reflection是一个非常强大的API,但是如果使用过多,它可能会减慢应用程序的运行速度,因为它会在运行时parsing所有types。
例如:
以一个远程应用程序为例,它为你的应用程序提供了一个你使用API方法获得的对象。 现在基于你可能需要执行某种计算的对象。
提供者保证对象可以是3种types,我们需要根据什么types的对象来执行计算。
所以我们可以在3个类中实现,每个类都包含一个不同的逻辑。显然,对象信息在运行时是可用的,所以你不能静态编码来执行计算,因此reflection被用来实例化你需要执行计算的类的对象。从提供者接收的对象。
Javareflection是相当强大的,可以是非常有用的。 Javareflection使得可以在运行时检查类,接口,字段和方法,而无需在编译时知道类名,方法等。 也可以实例化新对象,调用方法并使用reflection来获取/设置字段值。
一个快速的Javareflection示例,向您展示使用reflection的内容:
Method[] methods = MyObject.class.getMethods(); for(Method method : methods){ System.out.println("method = " + method.getName()); }
本示例从名为MyObject的类中获取Class对象。 使用类对象的例子获得该类中的方法列表,迭代方法并打印出他们的名字。
究竟如何所有这些工作在这里解释
编辑 :经过近一年的时间,我正在编辑这个答案,因为在阅读有关反思时,我得到了reflection的更多使用。
- Spring使用beanconfiguration,例如:
<bean id="someID" class="com.example.Foo"> <property name="someField" value="someValue" /> </bean>
当Spring上下文处理这个<bean>元素时,它将使用带有参数“com.example.Foo”的Class.forName(String)来实例化这个Class。
然后它将再次使用reflection来为<property>元素获取适当的setter,并将其值设置为指定的值。
- Junit尤其使用Reflection来testingPrivate / Protected方法。
对于私人方法,
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);
根据我的理解:
reflection允许程序员dynamic地访问程序中的实体。 即,如果程序员不知道类或其方法,则在编写应用程序的同时,可以通过使用reflectiondynamic地(在运行时)使用这样的类。
它经常用于类名变化频繁的场合。 如果出现这样的情况,程序员重写应用程序并且一次又一次地改变类名是很复杂的。
相反,通过使用reflection,需要担心可能改变的类名。
reflection的简单例子。 在国际象棋游戏中,您不知道用户在运行时将会移动什么。 reflection可以用来调用在运行时已经实现的方法。
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是一组函数,它允许您访问程序的运行时信息并修改它的行为(有一些限制)。
这很有用,因为它允许您根据程序的元信息更改运行时行为,也就是说,您可以检查函数的返回types并改变处理方式。
例如在C#中,您可以在运行时加载程序集(.dll),检查它,浏览类并根据您发现的内容采取措施。 它还允许您在运行时创build一个类的实例,调用它的方法等。
哪里可以有用? 除了具体的情况,每次都没用。 例如,你可以使用它来获取类的名称作为login的目的,根据configuration文件中指定的内容为事件创build事件处理程序,等等…
reflection是一个API,用于在运行时检查或修改方法,类,接口的行为。
- 在
java.lang.reflect package
中提供了所需的reflection类。 - reflection为我们提供了关于对象所属的类的信息,以及可以通过使用该对象来执行的那个类的方法。
- 通过reflection,我们可以在运行时调用方法,而不pipe用于它们的访问说明符。
java.lang
和java.lang.reflect
包为Javareflection提供了类。
reflection可以用来获取有关的信息 –
-
类
getClass()
方法用于获取对象所属类的名称。 -
构造函数
getConstructors()
方法用于获取对象所属的类的公共构造函数。 -
方法
getMethods()
方法用于获取对象所属的类的公共方法。
Reflection API主要用于:
IDE(集成开发环境),如Eclipse,MyEclipse,NetBeans等
debugging器和testing工具等
使用reflection的优点:
可扩展性function:应用程序可以使用外部的,用户定义的类,通过使用完全限定的名称创build可扩展性对象的实例。
debugging和testing工具:debugging器使用reflection属性来检查类上的私有成员。
缺点:
性能开销:reflection操作的性能比非reflection性操作的性能要差,应该避免在性能敏感的应用程序中频繁调用的代码部分。
内部曝光:reflection代码打破抽象,因此可能会改变平台升级的行为。
Ref: Java Reflection javarevisited.blogspot.in
从java文档页面
java.lang.reflect
包提供了类和接口来获得有关类和对象的reflection信息。 reflection允许编程访问有关加载类的字段,方法和构造函数的信息,以及在安全限制内使用反映的字段,方法和构造函数来对其基础对象进行操作。
如果必要的ReflectPermission
可用, AccessibleObject
允许禁止访问检查。
这个包中的类以及java.lang.Class
包含debugging器,解释器,对象检查器,类浏览器和Object Serialization
和JavaBeans
等服务,这些应用程序需要访问目标对象的公共成员(基于运行时类)或由给定类声明的成员
它包括以下function。
- 获取Class对象,
- 检查类(字段,方法,构造函数)的属性
- 设置和获取字段值,
- 调用方法,
- 创build对象的新实例。
查看这个由Class
类公开的方法的文档链接。
本文 (Sosnoski Software Solutions,Inc总裁Dennis Sosnoski)和本文 (安全探索pdf):
我可以看到比使用Reflection更多的缺点
反思用户:
- 它提供了dynamic链接程序组件的非常灵活的方式
- 用非常普遍的方式创build与对象一起工作的库是非常有用的
反思的缺点:
- 用于字段和方法访问时,reflection比直接代码慢得多。
- 它可以掩盖代码中实际发生的情况
- 它绕过源代码可能会造成维护问题
- reflection码也比相应的直接码更复杂
- 它允许违反关键的Java安全限制,如数据访问保护和types安全
一般滥用情况:
- 加载受限制的class级,
- 获取对受限制类的构造函数,方法或字段的引用,
- 创build新的对象实例,方法调用,获取或设置受限制类的字段值。
看看这个SE关于滥用reflectionfunction的问题:
我如何阅读Java中的私人领域?
概要:
在系统代码中不安全地使用其function也可能容易导致Java安全模式 l 的危害 。 所以请谨慎使用此function
我只想补充一点,列出所有。
使用Reflection API,您可以为任何对象编写通用的toString()
方法。
这在debugging中很有用。
这是一个例子:
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; } }
Reflection
有很多用途 。 我更熟悉的一个就是能够即时创build代码。
IE:dynamic类,函数,构造函数 – 基于任何数据(xml / array / sql results / hardcoded / etc ..)
除了提供在运行时dynamic调用方法创build实例的function之外,名称本身表明了它反映了它所持有的例如类方法等。
许多框架和应用程序都使用它来调用服务,而实际上并不知道代码。
反思就是让对象看到他们的外表。 这个说法似乎与反思无关。 其实这是“自我认同”的能力。
反思本身就是这样一种语言,它缺乏像Java和C#一样的自我认知和自我感知能力。 因为他们没有自我认识的能力,当我们想要观察它的样子时,我们必须有另外一件事来反映它的样子。 优秀的dynamic语言如Ruby和Python可以在没有其他人的帮助下感知自己的reflection。 我们可以说,Java的对象无法感觉到它没有镜像,它是reflection类的对象,但是Python中的对象可以毫无镜像地感知它。 所以这就是为什么我们需要Java中的reflection。
reflection使您能够编写更通用的代码。 它允许您在运行时创build对象并在运行时调用它的方法。 因此该程序可以做出高度参数化。 它也允许内省对象和类来检测暴露于外部世界的variables和方法。
Javareflection使得可以在运行时检查类,接口,字段和方法,而无需在编译时知道类名,方法等。 大多数在框架层面上,可以实现反思的最大好处。 在运行时需要额外修改的字节码用于在方法级别,实例variables级别,构造函数级别,注释级别reflection中检查,修改,添加更多的字节码,或者另外的程序或另外的框架。
假设你有一个方法add(Int a,int b)
。 等价的字节码是B1
。 如果假设在您的系统中有1000个名为add
方法。 现在,您需要在调用方法add
之前检查参数a
和b
的值。 因此,您可以将代码粘贴到另一个使用reflection的程序或框架,以使用Object.getClass.getMethod()
dynamic检查字节码值。 有几个类审查。 在调用方法add
之前,可以添加更多的操作。 但是,程序本身或其他程序或框架不知道具有名为add
的方法的对象。 大多数情况下,在dependency injection中,主要使用面向方面的编程使用reflection。