什么是反思,为什么它有用?
我对Java特别感兴趣,但我认为任何语言的原理都是一样的。
什么是反思,为什么它有用?
我对Java特别感兴趣,但我认为任何语言的原理都是一样的。
当前回答
反射是允许应用程序或框架处理可能尚未编写的代码的关键机制!
以典型的web.xml文件为例。这将包含一个servlet元素列表,其中包含嵌套的servlet类元素。servlet容器将处理web.xml文件,并通过反射创建每个servlet类的新实例。
另一个例子是Java API for XML Parsing(JAXP)。其中,XML解析器提供程序是通过众所周知的系统财产“插入”的,这些属性用于通过反射构造新实例。
最后,最全面的例子是Spring,它使用反射创建bean,并大量使用代理
其他回答
并非每种语言都支持反射,但在支持反射的语言中,原则通常是相同的。
反思是对程序结构进行“反思”的能力。或者更具体。查看您拥有的对象和类,并以编程方式获取有关它们实现的方法、字段和接口的信息。您还可以查看注释之类的内容。
它在很多情况下都很有用。您希望能够在代码中动态插入类的任何地方。许多对象关系映射器使用反射来实例化数据库中的对象,而无需事先知道它们将使用什么对象。插件架构是反射有用的另一个地方。在这些情况下,能够动态加载代码并确定是否有类型实现了作为插件使用的正确接口非常重要。
名称反射用于描述能够检查同一系统(或其本身)中其他代码的代码。
例如,假设您在Java中有一个未知类型的对象,如果存在,您希望对其调用“doSomething”方法。Java的静态类型系统并不是为了支持这一点而设计的,除非对象符合已知的接口,但是使用反射,您的代码可以查看对象并找出它是否有一个名为“doSomething”的方法,如果需要,可以调用它。
因此,给您一个Java代码示例(假设所讨论的对象是foo):
Method method = foo.getClass().getMethod("doSomething", null);
method.invoke(foo, null);
Java中一个非常常见的用例是注释的用法。例如,JUnit4将使用反射在类中查找标记有@Test注释的方法,然后在运行单元测试时调用它们。
有一些很好的反思示例可以帮助您开始http://docs.oracle.com/javase/tutorial/reflect/index.html
最后,是的,这些概念在其他支持反射的静态类型语言(如C#)中非常相似。在动态类型语言中,上面描述的用例不太必要(因为编译器将允许在任何对象上调用任何方法,如果不存在,则在运行时失败),但是第二种情况仍然很常见,即查找被标记或以某种方式工作的方法。
从评论更新:
检查系统中的代码并查看对象类型的能力是不是反思,而是类型反思。反射就是能够在运行时通过使用反省在某些语言中,这种区别是必要的支持内省,但不支持反思。一个这样的例子是C++
我觉得最好用例子来解释,但没有一个答案能做到这一点。。。
使用反射的一个实际例子是用Java编写的Java语言服务器或用PHP编写的PHP语言服务器等。语言服务器为您的IDE提供自动完成、跳转到定义、上下文帮助、提示类型等功能。为了让所有标记名(可以自动完成的单词)在键入时显示所有可能的匹配项,语言服务器必须检查类的所有内容,包括文档块和私有成员。为此,它需要对所述阶级的反思。
另一个例子是私有方法的单元测试。这样做的一种方法是创建一个反射,并在测试的设置阶段将方法的范围更改为公开。当然,有人会认为不应该直接测试私有方法,但这不是重点。
反射的简单示例。在国际象棋游戏中,您不知道用户在运行时会移动什么。反射可用于调用已在运行时实现的方法:
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文档页面
java.lang.reflect包提供了用于获取关于类和对象的反射信息的类和接口。反射允许编程访问有关已加载类的字段、方法和构造函数的信息,以及在安全限制内使用反射字段、方法或构造函数对其底层对应对象进行操作。
如果必要的ReflectPermission可用,AccessibleObject允许禁止访问检查。
此包中的类与java.lang.Class一起容纳调试器、解释器、对象检查器、类浏览器等应用程序,以及需要访问目标对象的公共成员(基于其运行时类)或给定类声明的成员的服务(如object Serialization和JavaBeans)
它包括以下功能。
获取类对象,检查类(字段、方法、构造函数)的财产,设置和获取字段值,调用方法,创建对象的新实例。
查看Class类公开的方法的文档链接。
本文(由Sosnoski Software Solutions,Inc总裁Dennis Sosnosiki撰写)和本文(security-explorations pdf):
与使用反射相比,我可以看到相当多的缺点
反射用户:
它提供了动态链接程序组件的多种方式它对于创建以非常通用的方式处理对象的库非常有用
反射的缺点:
当用于字段和方法访问时,反射比直接代码慢得多。它可以隐藏代码中的实际情况它绕过源代码会产生维护问题反射代码也比相应的直接代码更复杂它允许违反关键Java安全约束,例如作为数据访问保护和类型安全
一般滥用:
加载限制类,获取对受限类的构造函数、方法或字段的引用,创建新的对象实例、方法调用、获取或设置受限类的字段值。
看看这个关于滥用反射功能的SE问题:
如何在Java中读取私有字段?
摘要:
在系统代码中不安全地使用其功能也很容易导致Java安全模型的破坏。因此,谨慎使用此功能