什么是反思,为什么它有用?
我对Java特别感兴趣,但我认为任何语言的原理都是一样的。
什么是反思,为什么它有用?
我对Java特别感兴趣,但我认为任何语言的原理都是一样的。
当前回答
从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安全模型的破坏。因此,谨慎使用此功能
其他回答
我最喜欢的反射用法之一是下面的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();
}
从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安全模型的破坏。因此,谨慎使用此功能
Java反射非常强大,非常有用。Java反射使得在运行时检查类、接口、字段和方法成为可能,而不需要在编译时知道类、方法等的名称。还可以使用反射实例化新对象、调用方法和获取/设置字段值。
一个快速的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>元素时,它将使用Class.forName(String)和参数“com.example.Foo”来实例化该Class。
然后,它将再次使用反射为<property>元素获取适当的setter,并将其值设置为指定值。
Junit特别使用Reflection来测试Private/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);
我想举个例子来回答这个问题。首先,Hibernate项目使用反射API生成CRUD语句,以弥合运行应用程序和持久性存储之间的鸿沟。当域中的情况发生变化时,Hibernate必须了解这些情况,才能将其持久化到数据存储中,反之亦然。
另外,Lombok项目也可以。它只是在编译时注入代码,导致代码被插入到域类中。(我认为这对getter和setter来说是可以的)
Hibernate选择反射是因为它对应用程序的构建过程影响最小。
在Java7中,我们有MethodHandles,它用作反射API。在项目中,要使用记录器,我们只需复制粘贴下一个代码:
Logger LOGGER = Logger.getLogger(MethodHandles.lookup().lookupClass().getName());
因为在这种情况下很难出错。
名称反射用于描述能够检查同一系统(或其本身)中其他代码的代码。
例如,假设您在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++