什么是反思,为什么它有用?
我对Java特别感兴趣,但我认为任何语言的原理都是一样的。
什么是反思,为什么它有用?
我对Java特别感兴趣,但我认为任何语言的原理都是一样的。
当前回答
反射的使用
反射通常由需要检查或修改在Java虚拟机中运行的应用程序的运行时行为的程序使用。这是一个相对高级的特性,只有对语言基础有很强理解的开发人员才能使用。考虑到这一点,反射是一种强大的技术,可以使应用程序执行不可能执行的操作。
可扩展性功能
应用程序可以通过使用扩展性对象的完全限定名称创建扩展性对象实例来使用外部用户定义的类。类浏览器和可视化开发环境类浏览器需要能够枚举类的成员。可视化开发环境可以受益于利用反射中可用的类型信息来帮助开发人员编写正确的代码。调试器和测试工具调试器需要能够检查类中的私有成员。测试工具可以利用反射来系统地调用类上定义的可发现集合API,以确保测试套件中的代码覆盖率较高。
反射的缺点
反射是强大的,但不应滥用。如果可以在不使用反射的情况下执行一个操作,那么最好避免使用它。
性能开销
由于反射涉及动态解析的类型,因此无法执行某些Java虚拟机优化。因此,反射操作的性能比非反射操作慢,应该避免在性能敏感应用程序中频繁调用的代码段中使用。
安全性限制
反射需要在安全管理器下运行时可能不存在的运行时权限。对于必须在受限安全上下文(如Applet)中运行的代码,这是一个重要的考虑因素。
内部构件暴露
由于反射允许代码执行在非反射代码中非法的操作,例如访问私有字段和方法,因此使用反射可能会导致意外的副作用,这可能会导致代码功能失调,并可能破坏可移植性。反射代码打破了抽象,因此可能会随着平台的升级而改变行为。
来源:反射API
其他回答
反射是允许应用程序或框架处理可能尚未编写的代码的关键机制!
以典型的web.xml文件为例。这将包含一个servlet元素列表,其中包含嵌套的servlet类元素。servlet容器将处理web.xml文件,并通过反射创建每个servlet类的新实例。
另一个例子是Java API for XML Parsing(JAXP)。其中,XML解析器提供程序是通过众所周知的系统财产“插入”的,这些属性用于通过反射构造新实例。
最后,最全面的例子是Spring,它使用反射创建bean,并大量使用代理
反射有很多用途。我更熟悉的是能够动态创建代码。
IE:基于任何数据的动态类、函数、构造函数(xml/array/sql results/hardcoded/etc.)
我最喜欢的反射用法之一是下面的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();
}
根据我的理解:
反射允许程序员动态访问程序中的实体。也就是说,当程序员对应用程序进行编码时,如果不知道类或其方法,他可以通过使用反射来动态地(在运行时)使用此类类。
它经常用于类名频繁更改的场景。如果出现这种情况,那么程序员重写应用程序并一次又一次地更改类的名称是很复杂的。
相反,通过使用反射,需要担心类名可能会发生变化。
我想举个例子来回答这个问题。首先,Hibernate项目使用反射API生成CRUD语句,以弥合运行应用程序和持久性存储之间的鸿沟。当域中的情况发生变化时,Hibernate必须了解这些情况,才能将其持久化到数据存储中,反之亦然。
另外,Lombok项目也可以。它只是在编译时注入代码,导致代码被插入到域类中。(我认为这对getter和setter来说是可以的)
Hibernate选择反射是因为它对应用程序的构建过程影响最小。
在Java7中,我们有MethodHandles,它用作反射API。在项目中,要使用记录器,我们只需复制粘贴下一个代码:
Logger LOGGER = Logger.getLogger(MethodHandles.lookup().lookupClass().getName());
因为在这种情况下很难出错。