问:

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

我对 Java 特别感兴趣,但我认为原则在任何语言中都是相同的。

答1:

huntsbot.com汇聚了国内外优秀的初创产品创意,可按收入、分类等筛选,希望这些产品与实践经验能给您带来灵感。

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

例如,假设您在 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++

你能解释一下这一行中那个空参数的意义吗 Method method = foo.getClass().getMethod("doSomething", null);

null 表示没有参数传递给 foo 方法。有关详细信息,请参见 docs.oracle.com/javase/6/docs/api/java/lang/reflect/…,java.lang.Object...)。

只是为了澄清,因为这有很多赞成票。检查系统中的代码并查看对象类型的能力不是反射,而是类型自省。然后,反射是通过使用自省在运行时进行修改的能力。这里有必要进行区分,因为某些语言支持自省,但不支持反射。一个这样的例子是 C++。

我喜欢反射,但如果你可以控制代码,那么使用这个答案中指定的反射是不必要的,因此是一种滥用——你应该使用类型自省(instanceof)和强类型。如果除了反思之外还有其他方法可以做某事,那就应该这样做。反射会导致严重的心痛,因为您失去了使用静态类型语言的所有优势。如果您需要它,则需要它,但是即使那样,我也会考虑使用诸如 Spring 之类的预打包解决方案或完全封装必要反射的东西-IE:让其他人头疼。

@bigtunacan 你从哪里得到这些信息的?我看到 Oracle 官方 Java 文档中使用的术语“反射”不仅描述了在运行时进行更改的能力,还描述了查看对象类型的能力。更不用说大多数所谓的“类型自省”相关类(例如:Method、Constructor、Modifier、Field、Member,基本上显然除了 Class 之外的所有类)都在 {7 } 包裹。也许“反射”这个概念全面包括“类型自省”和运行时修改?

答2:

huntsbot.com洞察每一个产品背后的需求与收益,从而捕获灵感

反射是一种语言在运行时检查和动态调用类、方法、属性等的能力。

例如,Java 中的所有对象都有方法 getClass(),它可以让您确定对象的类,即使您在编译时不知道它(例如,如果您将其声明为 Object) - 这可能看起来微不足道,但这种反射在动态较少的语言(例如 C++)中是不可能的。更高级的用途可以让您列出和调用方法、构造函数等。

反射很重要,因为它可以让您编写不必在编译时“了解”所有内容的程序,从而使它们更具动态性,因为它们可以在运行时绑定在一起。可以针对已知接口编写代码,但是可以使用配置文件中的反射来实例化要使用的实际类。

由于这个原因,许多现代框架广泛使用反射。大多数其他现代语言也使用反射,并且在脚本语言(例如 Python)中它们的集成更加紧密,因为在这些语言的通用编程模型中感觉更加自然。

所以换句话说,你可以用它的限定名创建一个实例,编译器不会抱怨它(因为你只使用一个字符串作为类名)。然后,在运行时,如果该类不存在,则会出现异常。在这种情况下,您有点绕过编译器。你能给我一些具体的用例吗?我只是无法想象我什么时候会选择它。

@FernandoGabrieli 虽然使用反射很容易创建运行时错误,但也完全可以使用反射而不冒运行时异常的风险。正如我的回答所暗示的,反射的一个常见用途是库或框架,它们在编译时明确无法知道应用程序的结构,因为它们是与应用程序分开编译的。任何使用“约定代码”的库都可能使用反射,但不一定使用魔术字符串。

C++ 确实具有运行时类型信息。 RTTI

答3:

HuntsBot周刊–不定时分享成功产品案例,学习他们如何成功建立自己的副业–huntsbot.com

我最喜欢的反射用途之一是下面的 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();

}

Callcount 应该设置为什么?

运行此程序时,线程“AWT-EventQueue-0”java.lang.StackOverflowError 出现异常。

@Tom callCount 应设置为零。它的值用于确定每行输出之前应该有多少个制表符:每次转储需要转储“子对象”时,输出将打印为嵌套在父对象中。当包裹在另一个中时,该方法被证明是有用的。考虑 printDump(Object obj){ System.out.println(dump(obj, 0)); }。

由于未经检查的递归,可能会在循环引用的情况下创建 java.lang.StackOverflowError:buffer.append(dump(value, callCount))

你能把你的代码专门发布到公共领域吗?

答4:

huntsbot.com汇聚了国内外优秀的初创产品创意,可按收入、分类等筛选,希望这些产品与实践经验能给您带来灵感。

反射的用途

反射通常由需要能够检查或修改在 Java 虚拟机中运行的应用程序的运行时行为的程序使用。这是一个相对高级的特性,只应该由对语言基础有深入了解的开发人员使用。考虑到这一点,反射是一种强大的技术,可以使应用程序执行原本不可能的操作。

可扩展性功能

应用程序可以通过使用其完全限定名称创建可扩展性对象的实例来使用外部的、用户定义的类。类浏览器和可视化开发环境 类浏览器需要能够枚举类的成员。可视化开发环境可以受益于利用反射中可用的类型信息来帮助开发人员编写正确的代码。调试器和测试工具 调试器需要能够检查类中的私有成员。测试工具可以利用反射系统地调用定义在类上的可发现集 API,以确保测试套件中的高水平代码覆盖率。

反射的缺点

反射很强大,但不应该乱用。如果可以在不使用反射的情况下执行操作,那么最好避免使用它。通过反射访问代码时应牢记以下问题。

性能开销

由于反射涉及动态解析的类型,因此无法执行某些 Java 虚拟机优化。因此,反射操作的性能比它们的非反射对应物慢,应该避免在对性能敏感的应用程序中频繁调用的代码部分中。

安全限制

反射需要在安全管理器下运行时可能不存在的运行时权限。对于必须在受限安全上下文中运行的代码(例如在 Applet 中),这是一个重要的考虑因素。

内部暴露

由于反射允许代码执行在非反射代码中非法的操作,例如访问私有字段和方法,因此使用反射可能会导致意想不到的副作用,这可能会使代码功能失调并可能破坏可移植性。反射代码打破了抽象,因此可能会随着平台的升级而改变行为。

来源:The Reflection API

答5:

huntsbot.com高效搞钱,一站式跟进超10+任务平台外包需求

反射是允许应用程序或框架处理可能尚未编写的代码的关键机制!

以典型的 web.xml 文件为例。这将包含一个 servlet 元素列表,其中包含嵌套的 servlet 类元素。 servlet 容器将处理 web.xml 文件,并通过反射为每个 servlet 类创建一个新实例。

另一个示例是用于 XML 解析的 Java API (JAXP)。 XML 解析器提供程序通过众所周知的系统属性“插入”,这些属性用于通过反射构造新实例。

最后,最全面的例子是 Spring,它使用反射来创建它的 bean,并大量使用代理

答6:

huntsbot.com汇聚了国内外优秀的初创产品创意,可按收入、分类等筛选,希望这些产品与实践经验能给您带来灵感。

并非每种语言都支持反射,但支持反射的语言中的原理通常是相同的。

反思是“反思”程序结构的能力。或者更具体。查看您拥有的对象和类,并以编程方式获取有关它们实现的方法、字段和接口的信息。您还可以查看注释等内容。

它在很多情况下都很有用。您希望能够在代码中动态插入类的任何地方。许多对象关系映射器使用反射来实例化数据库中的对象,而无需事先知道它们将使用什么对象。插件架构是反射有用的另一个地方。在这些情况下,能够动态加载代码并确定是否存在实现正确接口以用作插件的类型很重要。

答7:

huntsbot.com – 高效赚钱,自由工作

反射允许在运行时动态地实例化新对象、调用方法以及对类变量进行获取/设置操作,而无需事先了解其实现。

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,但如果过度使用它可能会减慢应用程序的速度,因为它会在运行时解析所有类型。

答8:

huntsbot.com洞察每一个产品背后的需求与收益,从而捕获灵感

Java 反射非常强大并且非常有用。 Java 反射可以在运行时检查类、接口、字段和方法,而无需在编译时知道类、方法等的名称。也可以使用反射实例化新对象、调用方法和获取/设置字段值。

一个快速的 Java 反射示例,向您展示如何使用反射:

Method[] methods = MyObject.class.getMethods();

for(Method method : methods){

System.out.println("method = " + method.getName());

}

此示例从名为 MyObject 的类中获取 Class 对象。该示例使用类对象获取该类中的方法列表,迭代方法并打印出它们的名称。

Exactly how all this works is explained here

编辑:将近 1 年后,我正在编辑这个答案,因为在阅读反射时,我对反射有了更多的使用。

Spring 使用 bean 配置,例如:

当 Spring 上下文处理这个 元素时,它将使用带有参数“com.example.Foo”的 Class.forName(String) 来实例化该类。

然后它将再次使用反射来为 元素获取适当的设置器并将其值设置为指定值。

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);

答9:

huntsbot.com汇聚了国内外优秀的初创产品创意,可按收入、分类等筛选,希望这些产品与实践经验能给您带来灵感。

例子:

以一个远程应用程序为例,它为您的应用程序提供一个您使用其 API Methods 获得的对象。现在根据您可能需要执行某种计算的对象。

提供者保证 object 可以是 3 种类型,我们需要根据 object 的类型进行计算。

因此,我们可能会在 3 个类中实现,每个类都包含不同的逻辑。显然,对象信息在运行时可用,因此您不能静态编码来执行计算,因此反射用于实例化您需要基于从提供者收到的对象。

我需要类似的东西..一个例子会对我有很大帮助,因为我是反射概念的新手..

我很困惑:你不能在运行时使用 instanceof 来确定对象类型吗?

答10:

huntsbot.com – 高效赚钱,自由工作

反射的简单示例。在国际象棋游戏中,您不知道用户在运行时会移动什么。反射可用于调用已在运行时实现的方法:

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);

}

}

答11:

打造属于自己的副业,开启自由职业之旅,从huntsbot.com开始!

反射是一种 API,用于在运行时检查或修改方法、类、接口的行为。

java.lang.reflect 包下提供了反射所需的类。反射为我们提供了有关对象所属的类的信息,以及可以使用该对象执行的该类的方法。通过反射,我们可以在运行时调用方法,而不管它们使用的访问说明符如何。

java.lang 和 java.lang.reflect 包提供用于 Java 反射的类。

反射可用于获取有关以下内容的信息 -

类 getClass() 方法用于获取对象所属类的名称。构造函数 getConstructors() 方法用于获取对象所属类的公共构造函数。方法 getMethods() 方法用于获取对象所属类的公共方法。

反射 API 主要用于:

IDE(集成开发环境),例如 Eclipse、MyEclipse、NetBeans 等。调试器和测试工具等。

https://i.stack.imgur.com/zEXW5.png

使用反射的优点:

可扩展性特性:应用程序可以通过使用其完全限定名称创建可扩展性对象的实例来使用外部的、用户定义的类。

调试和测试工具:调试器使用反射属性来检查类的私有成员。

缺点:

性能开销:反射操作的性能比非反射操作慢,应避免在对性能敏感的应用程序中频繁调用的代码段中使用。

内部暴露:反射代码破坏了抽象,因此可能会随着平台的升级而改变行为。

参考:Java Reflection javarevisited.blogspot.in

我会补充缺点“It breaks refactoring”。对我来说,这是尽可能避免反思的主要原因。

所以它允许我们(例如)检查我们拥有的类(我们是否有它们的实例),对吗?我的意思是,获取它们的方法或构造函数并使用它们来创建新实例/调用它们。如果行为已经存在但使用不同的代码,为什么我们要说“改变程序行为”?为什么叫“反射”?谢谢

原文链接:https://www.huntsbot.com/qa/9r2Z/what-is-reflection-and-why-is-it-useful?lang=zh_CN&from=csdn

huntsbot.com精选全球7大洲远程工作机会,涵盖各领域,帮助想要远程工作的数字游民们能更精准、更高效的找到对方。

推荐阅读

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: