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

我对Java特别感兴趣,但我认为任何语言的原理都是一样的。


当前回答

反射是让物体看到它们的外观。这个论点似乎与反思无关。事实上,这就是“自我识别”能力。

反射本身是指Java和C#等缺乏自我认知和自我感知能力的语言。因为他们没有自知之明的能力,当我们想要观察它的样子时,我们必须有另一件事来反思它的样子。优秀的动态语言,如Ruby和Python,可以在没有其他人帮助的情况下感知自己的反射。我们可以说,Java的对象在没有镜像的情况下无法感知它的样子,镜像是反射类的对象,但Python中的对象可以在没有镜像时感知它。这就是为什么我们需要在Java中进行反射。

其他回答

根据我的理解:

反射允许程序员动态访问程序中的实体。也就是说,当程序员对应用程序进行编码时,如果不知道类或其方法,他可以通过使用反射来动态地(在运行时)使用此类类。

它经常用于类名频繁更改的场景。如果出现这种情况,那么程序员重写应用程序并一次又一次地更改类的名称是很复杂的。

相反,通过使用反射,需要担心类名可能会发生变化。

重要的

从Java9开始,您不能再使用反射,除非package-info.Java打开模块以进行反射访问。

默认情况下,拒绝对模块中的所有包进行“反射”访问。

请参阅了解Java 9模块

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

例如,假设您在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.lang.reflect包提供了用于获取关于类和对象的反射信息的类和接口。反射允许编程访问有关已加载类的字段、方法和构造函数的信息,以及在安全限制内使用反射字段、方法或构造函数对其底层对应对象进行操作。

如果必要的ReflectPermission可用,AccessibleObject允许禁止访问检查。

此包中的类与java.lang.Class一起容纳调试器、解释器、对象检查器、类浏览器等应用程序,以及需要访问目标对象的公共成员(基于其运行时类)或给定类声明的成员的服务(如object Serialization和JavaBeans)

它包括以下功能。

获取类对象,检查类(字段、方法、构造函数)的财产,设置和获取字段值,调用方法,创建对象的新实例。

查看Class类公开的方法的文档链接。

本文(由Sosnoski Software Solutions,Inc总裁Dennis Sosnosiki撰写)和本文(security-explorations pdf):

与使用反射相比,我可以看到相当多的缺点

反射用户:

它提供了动态链接程序组件的多种方式它对于创建以非常通用的方式处理对象的库非常有用

反射的缺点:

当用于字段和方法访问时,反射比直接代码慢得多。它可以隐藏代码中的实际情况它绕过源代码会产生维护问题反射代码也比相应的直接代码更复杂它允许违反关键Java安全约束,例如作为数据访问保护和类型安全

一般滥用:

加载限制类,获取对受限类的构造函数、方法或字段的引用,创建新的对象实例、方法调用、获取或设置受限类的字段值。

看看这个关于滥用反射功能的SE问题:

如何在Java中读取私有字段?

摘要:

在系统代码中不安全地使用其功能也很容易导致Java安全模型的破坏。因此,谨慎使用此功能

我想举个例子来回答这个问题。首先,Hibernate项目使用反射API生成CRUD语句,以弥合运行应用程序和持久性存储之间的鸿沟。当域中的情况发生变化时,Hibernate必须了解这些情况,才能将其持久化到数据存储中,反之亦然。

另外,Lombok项目也可以。它只是在编译时注入代码,导致代码被插入到域类中。(我认为这对getter和setter来说是可以的)

Hibernate选择反射是因为它对应用程序的构建过程影响最小。

在Java7中,我们有MethodHandles,它用作反射API。在项目中,要使用记录器,我们只需复制粘贴下一个代码:

Logger LOGGER = Logger.getLogger(MethodHandles.lookup().lookupClass().getName());

因为在这种情况下很难出错。