什么是反射,它为什么有用?
Posted
技术标签:
【中文标题】什么是反射,它为什么有用?【英文标题】:What is reflection and why is it useful? 【发布时间】:2010-09-07 10:45:11 【问题描述】:我对@987654322@ 特别感兴趣,但我认为任何语言的原则都是相同的。
【问题讨论】:
对我来说,这是一种在运行时获取类名并创建该类对象的方法。 因为这是一个流行的问题,所以我想指出反射(没有注释)应该是解决问题时使用的最后一个工具。我使用它并且喜欢它,但它抵消了 Java 静态类型的所有优点。如果您确实需要它,请将其隔离到尽可能小的区域(一个方法或一个类)。在测试中使用它比在生产代码中使用它更容易接受。使用注释应该没问题——如果可以避免的话,重点是不要将类或方法名指定为“字符串”。 另见:softwareengineering.stackexchange.com/questions/123956/… 除了@BillK 的评论:反射非常强大,我称之为魔法。拥有权利的同时也被赋予了重大的责任。仅当您知道自己在做什么时才使用它。 @Trap 我不知道,这就是我建议反对它的原因——当我遇到其他可用解决方案时的反射时,或者不是孤立于非常小的反射时,这真的很烦人, 受约束且清晰记录的代码区域。但是问程序员为什么要做他们所做的事情超出了我的回答能力。 【参考方案1】:名称反射用于描述能够检查同一系统(或自身)中的其他代码的代码。
例如,假设您在 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
之外的所有类)都在@987654330之内@ 包裹。或许“反射”这个概念综合包括“类型自省”和运行时修改?【参考方案2】:
反射是一组函数,允许您访问程序的运行时信息并修改它的行为(有一些限制)。
它很有用,因为它允许您根据程序的元信息更改运行时行为,即您可以检查函数的返回类型并更改处理情况的方式。
例如,在 C# 中,您可以在运行时加载程序集(一个 .dll)并对其进行检查,在类中导航并根据您发现的内容采取行动。它还允许您在运行时创建类的实例、调用其方法等。
它在哪里有用?并非每次都有用,但适用于具体情况。例如,您可以使用它来获取类的名称以进行日志记录,根据配置文件中指定的内容为事件动态创建处理程序等等......
【讨论】:
【参考方案3】:并非所有语言都支持反射,但支持反射的语言的原理通常是相同的。
反思是“反思”程序结构的能力。或者更具体。查看您拥有的对象和类,并以编程方式获取有关它们实现的方法、字段和接口的信息。您还可以查看注释之类的内容。
它在很多情况下都很有用。您希望能够在代码中动态插入类的任何地方。许多对象关系映射器使用反射来实例化数据库中的对象,而无需事先知道它们将使用什么对象。插件架构是反射有用的另一个地方。在这些情况下,能够动态加载代码并确定那里是否存在实现正确接口以用作插件的类型很重要。
【讨论】:
【参考方案4】:反射是一种语言在运行时检查和动态调用类、方法、属性等的能力。
例如,Java 中的所有对象都有 getClass()
方法,即使您在编译时不知道该对象的类(例如,如果您将其声明为 Object
),它也可以让您确定对象的类 - 这可能看起来微不足道,但在诸如C++
之类的动态性较低的语言中,这种反射是不可能的。更高级的用法让您可以列出和调用方法、构造函数等。
反射很重要,因为它让您编写的程序不必在编译时“了解”所有内容,从而使它们更具动态性,因为它们可以在运行时绑定在一起。可以针对已知接口编写代码,但要使用的实际类可以使用配置文件中的反射来实例化。
出于这个原因,许多现代框架广泛使用反射。大多数其他现代语言也使用反射,并且在脚本语言(例如 Python)中它们的集成更加紧密,因为在这些语言的通用编程模型中感觉更加自然。
【讨论】:
所以换句话说,你可以用它的限定名创建一个实例,编译器不会抱怨它(因为你只使用一个字符串作为类名)。然后,在运行时,如果该类不存在,则会出现异常。在这种情况下,您有点绕过编译器。你能给我一些具体的用例吗?我无法想象我什么时候会选择它。 @FernandoGabrieli 虽然使用反射很容易创建运行时错误,但也完全可以使用反射而不冒运行时异常的风险。正如我的回答所暗示的,反射的一个常见用途是库或框架,它们在编译时明确无法知道应用程序的结构,因为它们是与应用程序分开编译的。任何使用“约定代码”的库都可能使用反射,但不一定使用魔术字符串。C++
确实有运行时类型信息。 RTTI【参考方案5】:
反射是允许应用程序或框架处理可能尚未编写的代码的关键机制!
以典型的 web.xml 文件为例。这将包含一个 servlet 元素列表,其中包含嵌套的 servlet 类元素。 servlet 容器会处理 web.xml 文件,并通过反射为每个 servlet 类创建一个新的实例。
另一个例子是用于 XML 解析的 Java API (JAXP)。 XML 解析器提供程序通过众所周知的系统属性“插入”,这些属性用于通过反射构造新实例。
最后,最全面的例子是Spring,它使用反射来创建它的bean,并大量使用代理
【讨论】:
【参考方案6】:我最喜欢的反射用途之一是下面的 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.***Error 出现异常。 @TomcallCount
应设置为零。它的值用于确定每行输出之前应该有多少个制表符:每次转储需要转储一个“子对象”时,输出将打印为嵌套在父对象中。当包裹在另一个中时,该方法被证明是有用的。考虑printDump(Object obj) System.out.println(dump(obj, 0));
。
在循环引用的情况下可能会创建 java.lang.***Error,因为未检查的递归:buffer.append(dump(value, callCount))
您能具体将您的代码发布到公共领域吗?【参考方案7】:
据我了解:
反射允许程序员动态访问程序中的实体。即在编写应用程序时,如果程序员不知道某个类或其方法,他可以通过反射动态地(在运行时)使用此类。
常用于类名频繁变化的场景。如果出现这种情况,那么程序员重写应用程序和一次又一次地更改类的名称是很复杂的。
相反,通过使用反射,需要担心类名可能会发生变化。
【讨论】:
【参考方案8】:示例:
以一个远程应用程序为例,它为您的应用程序提供一个您使用其 API 方法获得的对象。现在根据您可能需要执行某种计算的对象。
提供者保证对象可以是3种类型,我们需要根据对象的类型进行计算。
所以我们可以在 3 个类中实现,每个类都包含不同的逻辑。显然,对象信息在运行时可用,因此您不能静态编码来执行计算,因此反射用于实例化执行计算所需的类的对象基于从提供者收到的对象。
【讨论】:
我需要类似的东西.. 一个例子对我很有帮助,因为我是反射概念的新手.. 我很困惑:你不能在运行时使用instanceof
来确定对象类型吗?【参考方案9】:
Reflection
有很多用途。我更熟悉的是能够动态创建代码。
IE:动态类、函数、构造函数——基于任何数据 (xml/array/sql results/hardcoded/etc..)
【讨论】:
如果您只给出一个从 SQL 结果或 XML 文件等生成的代码的不寻常示例,这个答案会好很多。 没问题。我在一个 Windows 应用程序中使用了反射,它根据从数据库中获取的 XML 动态生成接口。 所以基本上,我创建了一个向用户显示报告的类。该报告具有诸如日期(从到)ID 或其他任何参数之类的参数。此信息存储在 xml 中。所以首先我们有一个报告选择。根据选择的报告,表单获取 xml。检索到 xml 后,它使用反射创建一个类,其中包含基于反射类型的字段。更改为不同的报告后,将彻底清除石板,并根据 xml 生成新字段。所以它本质上是一种基于反射的动态形式。我也以其他方式使用过,但这应该足够了希望有帮助【参考方案10】:反射允许在运行时动态地实例化新对象、调用方法以及对类变量进行 get/set 操作,而无需事先了解其实现。
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。如果方法不是静态的,那么在调用时您需要提供一个有效的 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
访问。
Reflection 是一个非常强大的 API,但如果过度使用它可能会降低应用程序的速度,因为它会在运行时解析所有类型。
【讨论】:
【参考方案11】: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 配置,例如:<bean id="someID" class="com.example.Foo">
<property name="someField" value="someValue" />
</bean>
当 Spring 上下文处理这个
然后它将再次使用反射为
对于私有方法,
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);
【讨论】:
【参考方案12】:我只是想为列出的所有内容添加一些要点。
使用Reflection API,您可以为任何对象编写通用的toString()
方法。
在调试时很有用。
这里有一些例子:
class ObjectAnalyzer
private ArrayList<Object> visited = new ArrayList<Object>();
/**
* Converts an object to a string representation that lists all fields.
* @param obj an object
* @return a string with the object's class name and all field names and
* values
*/
public String toString(Object obj)
if (obj == null) return "null";
if (visited.contains(obj)) return "...";
visited.add(obj);
Class cl = obj.getClass();
if (cl == String.class) return (String) obj;
if (cl.isArray())
String r = cl.getComponentType() + "[]";
for (int i = 0; i < Array.getLength(obj); i++)
if (i > 0) r += ",";
Object val = Array.get(obj, i);
if (cl.getComponentType().isPrimitive()) r += val;
else r += toString(val);
return r + "";
String r = cl.getName();
// inspect the fields of this class and all superclasses
do
r += "[";
Field[] fields = cl.getDeclaredFields();
AccessibleObject.setAccessible(fields, true);
// get the names and values of all fields
for (Field f : fields)
if (!Modifier.isStatic(f.getModifiers()))
if (!r.endsWith("[")) r += ",";
r += f.getName() + "=";
try
Class t = f.getType();
Object val = f.get(obj);
if (t.isPrimitive()) r += val;
else r += toString(val);
catch (Exception e)
e.printStackTrace();
r += "]";
cl = cl.getSuperclass();
while (cl != null);
return r;
【讨论】:
【参考方案13】:反射的使用
反射通常由需要能够检查或修改在 Java 虚拟机中运行的应用程序的运行时行为的程序使用。这是一个相对高级的特性,只应该由对语言基础有深入了解的开发人员使用。考虑到这一点,反射是一种强大的技术,可以让应用程序执行原本不可能的操作。
可扩展性功能
应用程序可以通过使用其完全限定名称创建可扩展性对象的实例来使用外部的、用户定义的类。 类浏览器和可视化开发环境 类浏览器需要能够枚举类的成员。可视化开发环境可以受益于利用反射中可用的类型信息来帮助开发人员编写正确的代码。 调试器和测试工具 调试器需要能够检查类中的私有成员。测试工具可以利用反射系统地调用定义在类上的可发现集 API,以确保测试套件中的高水平代码覆盖率。
反射的缺点
反射很强大,但不应该乱用。如果可以在不使用反射的情况下执行操作,那么最好避免使用它。通过反射访问代码时应牢记以下注意事项。
性能开销由于反射涉及动态解析的类型,因此无法执行某些 Java 虚拟机优化。因此,反射操作的性能比非反射操作慢,应避免在对性能敏感的应用程序中频繁调用的代码段中使用。
安全限制反射需要运行时权限,在安全管理器下运行时可能不存在该权限。对于必须在受限安全上下文(例如 Applet)中运行的代码来说,这是一个重要的考虑因素。
内部结构暴露由于反射允许代码执行在非反射代码中非法的操作,例如访问私有字段和方法,因此使用反射可能会导致意外的副作用,这可能会导致代码功能失调并可能破坏可移植性。反射代码破坏了抽象,因此可能会随着平台的升级而改变行为。
来源:The Reflection API
【讨论】:
【参考方案14】:反射的简单示例。 在国际象棋游戏中,您不知道用户在运行时会移动什么。反射可用于调用已在运行时实现的方法:
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);
【讨论】:
【参考方案15】:反射就是让物体看到自己的样子。这个论点似乎与反思无关。其实这就是“自我认同”的能力。
Reflection 本身就是 Java 和 C# 等缺乏自我认识和自我感知能力的语言的一个词。因为他们没有自知之明的能力,所以当我们要观察它的样子时,我们必须有另一件事来反思它的样子。优秀的动态语言如 Ruby 和 Python 可以感知自己的反射,而无需其他人的帮助。可以说,Java 的对象没有镜子是无法感知它的样子,它是反射类的对象,但 Python 中的对象可以在没有镜子的情况下感知它。这就是为什么我们需要在 Java 中进行反射。
【讨论】:
type()、isinstance()、callable()、dir() 和 getattr()。 ....这些是pythonic反射调用【参考方案16】:来自java文档page
java.lang.reflect
包提供了类和接口,用于获取有关类和对象的反射信息。反射允许以编程方式访问有关已加载类的字段、方法和构造函数的信息,并允许在安全限制内使用反射字段、方法和构造函数对其底层对应项进行操作。
AccessibleObject
允许在必要的ReflectPermission
可用时禁止访问检查。
此包中的类以及java.lang.Class
可容纳调试器、解释器、对象检查器、类浏览器等应用程序,以及需要访问目标对象的公共成员的Object Serialization
和JavaBeans
等服务(基于其运行时类)或给定类声明的成员
它包括以下功能。
-
获取类对象,
检查类的属性(字段、方法、构造函数),
设置和获取字段值,
调用方法,
创建对象的新实例。
查看此documentation 链接,了解 Class
类公开的方法。
来自article(作者:Dennis Sosnoski,Sosnoski Software Solutions, Inc 总裁)和article(安全探索 pdf):
我发现使用反射比使用反射有很多缺点
反射用户:
-
它提供了非常通用的方式来动态链接程序组件
它对于创建以非常通用的方式处理对象的库很有用
反射的缺点:
-
在用于字段和方法访问时,反射比直接代码慢得多。
它会掩盖代码中实际发生的事情
绕过源代码会造成维护问题
反射代码也比对应的直接代码复杂
它允许违反关键的 Java 安全约束,例如
作为数据访问保护和类型安全
一般滥用:
-
加载受限类,
获取对受限类的构造函数、方法或字段的引用,
创建新对象实例、方法调用、获取或设置受限类的字段值。
看看这个关于滥用反射功能的 SE 问题:
How do I read a private field in Java?
总结:
在系统代码中不安全地使用其功能也很容易导致破坏 Java 安全模式l。 因此请谨慎使用此功能
【讨论】:
在某些情况下避免反射性能问题的一种方法是让一个类Woozle
在启动时检查其他类,看看哪些类有一个静态RegisterAsWoozleHelper()
方法,然后调用所有这些方法它发现有一个回调,他们可以用来告诉Woozle
关于他们自己,避免需要使用反射,例如反序列化数据。【参考方案17】:
反射使您能够编写更通用的代码。它允许您在运行时创建对象并在运行时调用其方法。因此程序可以高度参数化。它还允许对对象和类进行内省以检测其暴露于外部世界的变量和方法。
【讨论】:
【参考方案18】:Reflection 是一种 API,用于在运行时检查或修改方法、类、接口的行为。
java.lang.reflect package
下提供了反射所需的类。
反射为我们提供了有关对象所属类的信息,以及可以使用该对象执行的该类的方法。
通过反射,我们可以在运行时调用方法,而无需考虑与它们一起使用的访问说明符。
java.lang
和 java.lang.reflect
包为 Java 反射提供类。
Reflection 可用于获取有关 –
的信息类getClass()
方法用于获取对象所属类的名称。
构造函数getConstructors()
方法用于获取对象所属类的公共构造函数。
方法getMethods()
方法用于获取对象所属类的公共方法。
Reflection API主要用于:
IDE(集成开发环境)例如Eclipse、MyEclipse、NetBeans 等 调试器和测试工具等
使用反射的优点:
可扩展性特性:应用程序可以使用外部的、用户定义的类,方法是使用它们的完全限定名称创建可扩展性对象的实例。
调试和测试工具:调试器使用反射属性来检查类的私有成员。
缺点:
性能开销:反射操作的性能比非反射操作慢,应该避免在对性能敏感的应用程序中频繁调用的代码段中。
内部结构暴露:反射代码破坏了抽象,因此可能会随着平台的升级而改变行为。
参考:Java Reflectionjavarevisited.blogspot.in
【讨论】:
我会补充缺点“It breaks refactoring”。对我来说,这是尽可能避免反思的主要原因。 所以它允许我们(例如)检查我们拥有的类(无论我们是否有它们的实例),对吗?我的意思是,获取他们的方法或构造函数并使用它们来创建新实例/调用它们。如果行为已经存在但使用不同的代码,为什么我们要说“改变程序行为”?为什么叫“反射”?谢谢【参考方案19】:顾名思义,它反映了它所拥有的例如类方法等,除了提供在运行时动态调用方法创建实例的功能。
许多框架和应用程序都使用它来调用服务,而无需真正了解代码。
【讨论】:
【参考方案20】:我想通过例子来回答这个问题。首先Hibernate
项目使用Reflection API
生成CRUD
语句来弥合正在运行的应用程序和持久性存储之间的鸿沟。当域中的事情发生变化时,Hibernate
必须知道它们以将它们持久化到数据存储中,反之亦然。
也可以使用Lombok Project
。它只是在编译时注入代码,导致代码被插入到您的域类中。 (我认为 getter 和 setter 都可以)
Hibernate
选择 reflection
是因为它对应用程序的构建过程影响最小。
从 Java 7 开始,我们有了 MethodHandles
,它作为 Reflection API
工作。在项目中,要使用记录器,我们只需复制粘贴以下代码:
Logger LOGGER = Logger.getLogger(MethodHandles.lookup().lookupClass().getName());
因为在这种情况下很难出现拼写错误。
【讨论】:
【参考方案21】:我觉得最好通过例子来解释,但似乎没有一个答案能做到这一点......
使用反射的一个实际示例是用 Java 编写的 Java 语言服务器或用 php 编写的 PHP 语言服务器等。语言服务器为您的 IDE 提供自动完成、跳转到定义、上下文帮助、提示类型等功能。为了让所有标签名称(可以自动补全的单词)在您键入时显示所有可能的匹配项,Language Server 必须检查有关该类的所有内容,包括文档块和私有成员。为此,它需要反映所述类。
另一个例子是私有方法的单元测试。一种方法是在测试的设置阶段创建反射并将方法的范围更改为公共。当然有人会争辩说不应该直接测试私有方法,但这不是重点。
【讨论】:
【参考方案22】:重要
从 Java 9 开始,您不能再使用反射,除非 package-info.java 打开模块以进行反射访问。
默认情况下,模块中的所有包都拒绝“反射”访问。
见Understanding Java 9 Modules
【讨论】:
这是完全错误的。您仍然可以使用反射。如果包裹对您开放,您将无法访问 (ref.setAccessible(true);
)。以上是关于什么是反射,它为什么有用?的主要内容,如果未能解决你的问题,请参考以下文章