什么是反射,为什么有用?
我对Java特别感兴趣,但是我认为原理在任何语言中都是相同的。
#1楼
例:
以一个远程应用程序为例,该应用程序为您的应用程序提供了一个对象,该对象是使用其API方法获得的。 现在,基于对象,您可能需要执行某种计算。
提供者保证对象可以是3种类型,我们需要根据对象的类型进行计算。
因此我们可以在3个类中实现,每个类包含不同的逻辑。显然,对象信息在运行时可用,因此您不能静态地执行代码,因此反射被用于实例化基于该类执行计算所需的对象。从提供者接收的对象。
#2楼
Reflection
有很多用途 。 我比较熟悉的一种是能够即时创建代码。
IE:动态类,函数,构造函数-基于任何数据(xml / array / sql结果/ hardcoded /等)。
#3楼
反射允许在运行时动态地实例化新对象,方法调用以及对类变量进行获取/设置操作,而无需事先了解其实现。
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。
反射还允许您访问类的私有成员/方法:
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,但是如果使用过多,它可能会使应用程序变慢,因为它可以在运行时解析所有类型。
#4楼
Java Reflection功能非常强大,并且非常有用。 Java Reflection使得在运行时检查类,接口,字段和方法成为可能,而无需在编译时知道类,方法等的名称。 还可以使用反射实例化新对象,调用方法并获取/设置字段值。
一个简单的Java反射示例向您展示如何使用反射:
Method[] methods = MyObject.class.getMethods();
for(Method method : methods){
System.out.println("method = " + method.getName());
}
本示例从名为MyObject的类获取Class对象。 该示例使用类对象获取该类中方法的列表,迭代这些方法并打印出它们的名称。
编辑 :将近一年后,我正在编辑此答案,因为在阅读有关反射的文章时,我对反射的使用很少。
- Spring使用bean配置,例如:
<bean id="someID" class="com.example.Foo">
<property name="someField" value="someValue" />
</bean>
当Spring上下文处理此<bean>元素时,它将使用带有参数“ com.example.Foo”的Class.forName(String)实例化该Class。
然后,它将再次使用反射来获取<property>元素的适当的setter并将其值设置为指定的值。
- Junit特别使用反射来测试专用/受保护的方法。
对于私有方法,
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);
#5楼
我只想向列出的所有内容添加一些要点。
使用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;
}
}
#6楼
反射的用途
反射通常由需要检查或修改Java虚拟机中运行的应用程序的运行时行为的程序使用。 这是一个相对高级的功能,只应由对语言基础有很深了解的开发人员使用。 考虑到这一警告,反射是一种强大的技术,可以使应用程序执行原本不可能的操作。
扩展功能
应用程序可以通过使用其完全限定的名称创建可扩展性对象的实例来使用外部用户定义的类。 类浏览器和可视化开发环境类浏览器需要能够枚举类的成员。 可视化开发环境可以受益于利用反射中可用的类型信息来帮助开发人员编写正确的代码。 调试器和测试工具调试器需要能够检查类中的私有成员。 测试工具可以利用反射来系统地调用在类上定义的可发现集合API,以确保测试套件中的代码覆盖率很高。
反思的缺点
反射功能强大,但不应任意使用。 如果可以在不使用反射的情况下执行操作,那么最好避免使用它。 通过反射访问代码时,应牢记以下注意事项。
- 绩效开销
由于反射涉及动态解析的类型,因此无法执行某些Java虚拟机优化。 因此,反射操作的性能比非反射操作慢,因此应避免在对性能敏感的应用程序中经常调用的代码段中。
- 安全限制
反射需要运行时许可,而在安全管理器下运行时可能不存在。 对于必须在受限的安全上下文(例如Applet)中运行的代码,这是一个重要的考虑因素。
- 内部暴露
由于反射允许代码执行非反射代码中非法的操作(例如访问私有字段和方法),因此使用反射可能会导致意外的副作用,这可能会使代码无法正常工作并可能破坏可移植性。 反射代码破坏了抽象,因此可能会随着平台的升级而改变行为。
来源: Reflection API
#7楼
反映的简单示例。 在国际象棋游戏中,您不知道用户在运行时将移动什么。 反射可用于调用在运行时已实现的方法:
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);
}
}
#8楼
反射是让物体看到它们的外观。 这种说法似乎与反思无关。 实际上,这就是“自我识别”能力。
反射本身就是这类语言的缩写,因为它们缺乏像Java和C#这样的自我知识和自我感知能力。 因为他们没有自我认识的能力,所以当我们要观察它的外观时,我们必须有另一件事来反思它的外观。 诸如Ruby和Python之类的出色动态语言无需其他人的帮助即可感知自己的反映。 我们可以说Java对象不能在没有镜像的情况下感知它的外观,而镜像是反射类的对象,但是Python中的对象可以在没有镜像的情况下感知它。 所以这就是为什么我们需要在Java中进行反思。
#9楼
从Java文档页面
java.lang.reflect
包提供用于获取有关类和对象的反射信息的类和接口。 通过反射,可以在安全性限制内以编程方式访问有关已加载类的字段,方法和构造函数的信息,并可以使用反射的字段,方法和构造函数对其基础副本进行操作。
如果必要的ReflectPermission
可用, AccessibleObject
允许禁止访问检查。
此软件包中的类以及java.lang.Class
容纳需要访问目标对象的公共成员(基于其对象)的应用程序,例如调试器,解释器,对象检查器,类浏览器以及服务,例如Object Serialization
和JavaBeans
。运行时类)或给定类声明的成员
它包括以下功能。
- 获取Class对象,
- 检查类的属性(字段,方法,构造函数),
- 设置和获取字段值
- 调用方法
- 创建对象的新实例。
查看此文档链接,了解Class
类公开的方法。
从本文 (Sosnoski Software Solutions,Inc.总裁Dennis Sosnoski)和本文 (安全性探索pdf)开始:
与使用反射相比,我可以看到很多弊端
反射用户:
- 它提供了非常通用的动态链接程序组件的方式
- 这对于创建以非常通用的方式使用对象的库很有用
反思的缺点:
- 当用于字段和方法访问时,反射比直接代码慢得多。
- 它可以掩盖代码内部的实际情况
- 它绕过源代码会造成维护问题
- 反射代码也比相应的直接代码复杂
- 它允许违反关键的Java安全约束,例如数据访问保护和类型安全
一般滥用行为:
- 加载受限类,
- 获取对受限类的构造函数,方法或字段的引用,
- 创建新的对象实例,方法调用,获取或设置受限类的字段值。
看一下有关反射功能滥用的SE问题:
摘要:
从系统代码内部对其功能的不安全使用也很容易导致Java安全模式的妥协 。 因此,请谨慎使用此功能
#10楼
反射使您能够编写更多通用代码。 它允许您在运行时创建对象并在运行时调用其方法。 因此,可以使程序高度参数化。 它还允许对对象和类进行自省,以检测暴露给外界的变量和方法。
#11楼
名称反射用于描述能够检查同一系统(或本身)中其他代码的代码。
例如,假设您在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 ++就是一个这样的例子。
#12楼
反射是一组功能,使您可以访问程序的运行时信息并修改其行为(有一些限制)。
这很有用,因为它允许您根据程序的元信息来更改运行时行为,即可以检查函数的返回类型并更改处理情况的方式。
例如,在C#中,您可以在运行时加载程序集(.dll),然后对其进行检查,浏览类并根据发现的内容采取措施。 它还允许您在运行时创建类的实例,调用其方法等。
在哪里有用? 并非每次都有用,但适用于具体情况。 例如,您可以使用它来获取用于登录目的的类的名称,根据配置文件上指定的内容为事件动态创建处理程序,等等。
#13楼
并非每种语言都支持反射,但是支持反射的语言的原理通常是相同的。
反射是“反射”程序结构的能力。 或更具体。 要查看您拥有的对象和类,并以编程方式获取有关它们实现的方法,字段和接口的信息。 您还可以查看注释等内容。
在许多情况下很有用。 您希望能够在任何地方将类动态地插入代码中。 很多对象关系映射器都使用反射功能来实例化数据库中的对象,而无需事先知道它们将使用什么对象。 插件体系结构是反射有用的另一个地方。 在这些情况下,能够动态加载代码并确定其中是否存在实现正确接口以用作插件的类型非常重要。
#14楼
反射是语言在运行时检查和动态调用类,方法,属性等的能力。
例如,Java中的所有对象都具有方法getClass()
,即使您在编译时不知道该对象的类(例如,如果您将其声明为Object
),也可以通过它来确定该对象的类-这似乎很简单,但是这样在不太动态的语言(例如C++
反射是不可能的。 更高级的用法使您可以列出和调用方法,构造函数等。
反射很重要,因为它使您可以编写在编译时不必“了解”所有程序的程序,从而使它们更具动态性,因为它们可以在运行时绑定在一起。 可以针对已知接口编写代码,但是可以使用配置文件中的反射实例化要使用的实际类。
正因为如此,许多现代框架广泛使用反射。 其他大多数现代语言也都使用反射,在脚本语言(例如Python)中,它们之间的集成更加紧密,因为在这些语言的通用编程模型中感觉更自然。
#15楼
反射是一种允许应用程序或框架使用甚至可能尚未编写的代码的关键机制!
以典型的web.xml文件为例。 这将包含servlet元素列表,其中包含嵌套的servlet类元素。 Servlet容器将处理web.xml文件,并通过反射为每个Servlet类创建新的新实例。
另一个示例是用于XML解析的Java API (JAXP) 。 通过众所周知的系统属性“插入” XML解析器提供程序的位置,该属性用于通过反射构造新实例。
最后,最全面的示例是Spring ,它使用反射来创建其bean,并大量使用代理
#16楼
我最喜欢的反射用法之一是下面的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();
}
#17楼
反射是一种API,用于在运行时检查或修改方法,类,接口的行为。
- 所需的反射类在
java.lang.reflect package
下提供。 - 反射为我们提供了有关对象所属类的信息,以及可以使用该对象执行的该类的方法。
- 通过反射,我们可以在运行时调用方法,而与它们所使用的访问说明符无关。
java.lang
和java.lang.reflect
包提供了Java反射的类。
反射可用于获取有关–的信息
类
getClass()
方法用于获取对象所属的类的名称。构造函数
getConstructors()
方法用于获取对象所属类的公共构造函数。方法
getMethods()
方法用于获取对象所属类的公共方法。
Reflection API主要用于:
IDE(集成开发环境),例如Eclipse,MyEclipse,NetBeans等。
调试器和测试工具等
使用反射的优势:
可扩展性功能:应用程序可以通过使用其完全限定的名称创建可扩展性对象的实例来使用外部的用户定义类。
调试和测试工具:调试器使用反射的属性检查类的私有成员。
缺点:
性能开销:反射性操作的性能比非反射性操作慢,因此应避免在对性能敏感的应用程序中经常调用的代码段中避免这样做。
内部曝光:反射代码破坏了抽象,因此可能会随着平台升级而改变行为。
参考: Java Reflection javarevisited.blogspot.in
#18楼
顾名思义,它反映了它在例如类方法等中的作用,除了提供在运行时动态调用创建实例的方法的功能外。
许多框架和应用程序使用它在不实际知道代码的情况下调用服务。
#19楼
我想举例说明这个问题。 首先, Hibernate
项目使用Reflection API
生成CRUD
语句,以桥接正在运行的应用程序和持久性存储之间的鸿沟。 当域中的事物发生变化时, Hibernate
必须了解它们,才能将其持久保存到数据存储中,反之亦然。
替代工程Lombok Project
。 它只是在编译时注入代码,导致代码被插入到您的域类中。 (我认为对getter和setter来说还可以)
Hibernate
之所以选择reflection
是因为它对应用程序的构建过程影响很小。
从Java 7开始,我们有了MethodHandles
,它可以用作Reflection API
。 在项目中,要与记录器一起使用,我们只需复制粘贴以下代码:
Logger LOGGER = Logger.getLogger(MethodHandles.lookup().lookupClass().getName());
因为在这种情况下很难犯错字。
#20楼
我发现最好通过示例进行解释,但似乎没有答案可以解决这个问题。
使用反射的一个实际示例是用Java编写的Java语言服务器或用PHP编写的PHP语言服务器等。语言服务器为您的IDE提供了诸如自动完成,跳转到定义,上下文帮助,提示类型等功能。 为了使所有标签名称(可以自动完成的单词)在您键入时显示所有可能的匹配,语言服务器必须检查有关该类的所有内容,包括文档块和私有成员。 为此,它需要反映所述类。
一个不同的例子是私有方法的单元测试。 这样做的一种方法是在测试的设置阶段创建反射并将方法的范围更改为公开。 当然可以说私有方法不应直接测试,但这不是重点。
#21楼
根据我的理解:
反射允许程序员动态访问程序中的实体。 例如,如果程序员不知道类或其方法,则在编写应用程序时,他可以通过反射来动态地(在运行时)使用此类。
它经常用于类名频繁更改的场景中。 如果出现这种情况,那么程序员重写应用程序并一次又一次更改类的名称很复杂。
相反,通过使用反射,需要担心类名称可能会更改。