调用方法
原文:
反射提供了一种在类上调用方法的方式。通常,只有在非反射代码中无法将类的实例强制转换为所需类型时才需要这样做。方法是使用来调用的。第一个参数是要调用该特定方法的对象实例。(如果方法是,第一个参数应为。)后续参数是方法的参数。如果底层方法抛出异常,它将被包装。可以使用异常链接机制的方法检索方法的原始异常。
查找和调用具有特定声明的方法
考虑一个测试套件,它使用反射来调用给定类中的私有测试方法。示例搜索类中以字符串""开头,具有布尔返回类型和单个参数的方法。然后调用每个匹配的方法。
调用,它将返回类中明确声明的所有方法。此外,使用来确定定位方法的参数是否与所需调用兼容。从技术上讲,代码可以测试以下语句是否为,因为是的:
讯享网
然而,更通用。
讯享网
首先,请注意只有符合代码强制执行的声明限制。接下来,当传递无效参数时,它会抛出一个未经检查的。在反射中,对于已检查和未检查的异常处理没有区别。它们都被包装在一个中。
调用具有可变数量参数的方法
可用于向方法传递可变数量的参数。要理解的关键概念是,可变参数的方法被实现为如果可变参数被打包在一个数组中。
示例演示了如何调用任何类中的入口点,并在运行时传递一组参数。
首先,为了找到方法,代码会搜索一个名为"main"的类,该类有一个参数,参数是一个数组。由于是的,是传递给的第一个参数。第二个参数是要传递的参数数组。
故障排除
原文:
本节包含开发人员在使用反射定位、调用或获取方法时可能遇到的问题示例。
由于类型擦除导致的 NoSuchMethodException
示例说明了当代码在类中搜索特定方法时未考虑类型擦除时会发生什么。
当方法声明具有泛型参数类型时,编译器将使用其上界替换泛型类型,在本例中,的上界为。因此,当代码搜索时,尽管的实例是如下创建的,但不会找到方法:
搜索成功,如预期。
在这种情况下,没有泛型参数,因此搜索的参数类型必须完全匹配。
提示: 在搜索方法时,始终传递参数化类型的上界。
调用方法时的 IllegalAccessException
如果尝试调用或其他不可访问方法,则会抛出。
示例展示了尝试在另一个类中调用私有方法导致的典型堆栈跟踪。
异常抛出的堆栈跟踪如下。
提示: 存在访问限制,阻止对通常无法通过直接调用访问的方法进行反射调用。(包括但不限于在另一个类中的方法和在另一个私有类中的公共方法。)但是,被声明为扩展,通过提供了抑制此检查的能力。如果成功,则随后对该方法对象的调用不会因此问题而失败。
从 Method.invoke()中抛出的 IllegalArgumentException
已经被改造成为可变参数方法。这是一个巨大的便利,但可能会导致意外行为。示例展示了可能产生混乱结果的各种方式。
2022最新java基础教程
由于的所有参数都是可选的,除了第一个,当要调用的方法没有参数时,可以省略它们。
在这种情况下,代码生成这个编译器警告,因为是模棱两可的。
不可能确定代表空参数数组还是第一个参数为。
尽管参数为,但这会失败,因为类型是,而不需要任何参数。
这能够成功是因为创建了一个空数组,对于可变参数方法来说,这等同于不传递任何可选参数。
与前面的例子不同,如果空数组存储在一个中,那么它将被视为一个。这与案例 2 失败的原因相同,不期望有参数。
提示:当声明一个方法时,编译器会将传递给的所有参数放入一个类型的数组中。的实现与声明为时相同。理解这一点可能有助于避免上面所示问题的类型。
调用方法失败时的 InvocationTargetException
包装了调用方法对象时产生的所有异常(已检查和未检查)。示例展示了如何检索被调用方法抛出的原始异常。
提示:如果抛出,则方法已被调用。诊断问题的方法与直接调用方法并通过检索到的异常相同。此异常并不表示反射包或其使用存在问题。
构造函数
原文:
构造函数 用于创建一个属于类的实例的对象。通常在调用方法或访问字段之前执行初始化类所需的操作。构造函数不会被继承。
与方法类似,反射提供了 API 来发现和检索类的构造函数,并获取声明信息,如修饰符、参数、注解和抛出的异常。还可以使用指定的构造函数创建类的新实例。在处理构造函数时使用的关键类是 和 。涵盖了涉及构造函数的常见操作的以下部分:
- 查找构造函数 说明了如何检索具有特定参数的构造函数
- 检索和解析构造函数修饰符 展示了如何获取构造函数声明的修饰符以及有关构造函数的其他信息
- 创建新的类实例 展示了如何通过调用其构造函数实例化对象的实例
- 故障排除 描述了在查找或调用构造函数时可能遇到的常见错误
查找构造函数
原文:
构造函数声明包括名称、修饰符、参数和可抛出异常列表。类提供了获取这些信息的方法。
示例演示了如何搜索一个类的声明构造函数中具有给定类型参数的构造函数。
将在类文件中的签名属性中查找(如果存在)。如果属性不可用,则会回退到,这个方法在引入泛型之前并未更改。其他以命名的反射方法也是类似实现的。返回值的语法在中有描述。
这里是所有在中具有参数的构造函数的输出。
下一个示例输出演示了如何在中搜索类型为的参数。
表达接受的引用和基本类型数组的语法在中有描述。请注意,第一个列出的构造函数是的,而不是的。它被返回是因为示例代码使用了而不是,后者只返回构造函数。
这个示例表明,搜索可变参数的参数(具有可变数量的参数)需要使用数组语法:
这是源代码中构造函数的实际声明:
参数表示为类型为的单维数组。可以通过调用来区分明确为数组的参数。
最后一个示例报告了已声明具有泛型参数类型的构造函数的输出:
与方法类似,可以以类似的方式检索构造函数的异常类型。有关更多详细信息,请参见示例中描述的获取方法类型信息部分。
检索和解析构造函数修饰符
原文:
由于构造函数在语言中的作用,比方法更少的修饰符是有意义的:
- 访问修饰符:,和
- 注解
示例在给定类中搜索具有指定访问修饰符的构造函数。它还显示构造函数是否是合成的(由编译器生成)或具有可变参数。
没有明确对应于“包私有”访问权限的常量,因此需要检查所有三个访问修饰符的缺失来识别包私有构造函数。
此输出显示了中的私有构造函数:
合成构造函数很少见;但是示例说明了可能发生这种情况的典型情况:
由于内部类的构造函数引用了封闭类的私有构造函数,编译器必须生成一个包私有构造函数。参数类型是任意的,取决于编译器的实现。依赖于任何合成或非公共类成员存在的代码可能不具有可移植性。
构造函数实现了,提供了用于检索运行时注解的方法,使用。有关获取注解的示例,请参见检查类修饰符和类型部分。
创建新的类实例
原文:
创建类实例的两种反射方法: 和 。前者更受青睐,因此在这些示例中使用,原因如下:
- 只能调用零参数构造函数,而可以调用任何构造函数,无论参数个数如何。
- 无论构造函数抛出的是已检查异常还是未检查异常,都会抛出该异常。总是用包装抛出的异常。
- 要求构造函数可见;在某些情况下可以调用构造函数。
有时可能希望从仅在构造后设置的对象中检索内部状态。考虑一个场景,需要获取使用的内部字符集。(字符集存储在私有字段中,并且不一定与返回的 Java 虚拟机默认字符集相同)。示例展示了如何实现这一点:
注意:
如果构造函数没有参数且已经可访问,则才会成功。否则,需要像上面的示例一样使用。
UNIX 系统的示例输出:
Windows 系统的示例输出:
另一个常见的 应用是调用需要参数的构造函数。 示例找到一个特定的单参数构造函数并调用它:
这个示例使用 来找到一个参数类型为 的构造函数。请注意,只需传递 就足够了,因为任何 方法的参数只需要类来确定类型。由于 类型擦除,以下表达式求值为 :
然后,示例使用这个构造函数使用 创建类的新实例。
故障排除
原文:
开发人员在尝试通过反射调用构造函数时,有时会遇到以下问题。
由于缺少零参数构造函数而导致的 InstantiationException
示例说明了当代码尝试使用创建类的新实例时,且没有可访问的零参数构造函数时会发生什么:
提示:可能发生的原因有很多。在这种情况下,问题在于具有参数的构造函数的存在阻止了编译器生成默认(或零参数)构造函数,并且代码中没有显式的零参数构造函数。请记住,的行为非常类似于关键字,只要失败,它就会失败。
Class.newInstance() 抛出意外异常
示例展示了在中出现的无法解决的问题。即,它传播构造函数抛出的任何异常(已检查或未检查)。
这种情况是反射独有的。通常情况下,不可能编写忽略已检查异常的代码,因为这样的代码不会编译。可以通过使用而不是来包装构造函数抛出的任何异常。
如果抛出,则表示方法已被调用。对问题的诊断与直接调用构造函数并抛出异常,然后通过检索到的异常相同。此异常并不表示反射包或其使用存在问题。
提示:最好使用而不是,因为前者的 API 允许检查和处理构造函数抛出的任意异常。
定位或调用正确构造函数的问题
类展示了代码错误可能无法定位或调用预期构造函数的各种方式。
抛出是因为请求零参数构造函数并尝试传递参数。如果构造函数传递了错误类型的参数,也会抛出相同的异常。

如果开发人员错误地认为反射会自动装箱或拆箱类型,则可能会出现此异常。装箱(将原始类型转换为引用类型)仅在编译期间发生。在反射中没有机会进行此操作,因此在定位构造函数时必须使用特定类型。
在这里,可能期望调用接受参数的构造函数,因为使用了更具体的类型调用了。然而,为时已晚!找到的构造函数已经是接受参数的构造函数。不会尝试进行方法解析;它只是在现有构造函数对象上操作。
提示: 和之间的一个重要区别是,执行方法参数类型检查、装箱和方法解析。在反射中,这些都不会发生,必须做出明确选择。
尝试调用不可访问构造函数时出现 IllegalAccessException
如果尝试调用私有或其他不可访问的构造函数,则可能会抛出。示例展示了产生的堆栈跟踪。
提示: 存在访问限制,阻止通过直接调用无法访问的构造函数进行反射调用。(这包括但不限于在单独类中的私有构造函数和在单独私有类中的公共构造函数。)但是,被声明为扩展,它提供了通过来抑制此检查的能力。
课程:数组和枚举类型
原文:
从 Java 虚拟机的角度看,数组和枚举类型(或枚举)只是类。许多 中的方法可以用于它们。反射为数组和枚举提供了一些特定的 API。本课程使用一系列代码示例来描述如何区分这些对象与其他类,并对其进行操作。还将检查各种错误。
数组
数组有一个组件类型和一个长度(长度不是类型的一部分)。数组可以整体操作,也可以逐个组件操作。反射为后者提供了 类。
- 识别数组类型 描述了如何确定类成员是否是数组类型的字段
- 创建新数组 演示了如何创建具有简单和复杂组件类型的新数组实例
- 获取和设置数组及其组件 展示了如何访问数组类型的字段以及单独访问数组元素
- 故障排除 包括常见错误和编程误解
枚举类型
在反射代码中,枚举类型与普通类非常相似。 可以告诉一个 是否表示一个 。 可以检索在枚举中定义的枚举常量。 表示一个字段是否是一个枚举类型。
- 检查枚举 演示了如何检索枚举的常量以及任何其他字段、构造函数和方法
- 使用枚举类型获取和设置字段 展示了如何设置和获取具有枚举常量值的字段
- 故障排除 描述了与枚举相关的常见错误
数组
原文:
一个数组是引用类型的对象,包含固定数量的相同类型的组件;数组的长度是不可变的。创建数组的实例需要知道长度和组件类型。每个组件可以是原始类型(如、或),引用类型(如、或),或者是数组。多维数组实际上只是包含数组类型组件的数组。
数组在 Java 虚拟机中实现。数组上的唯一方法是从继承的方法。数组的长度不是其类型的一部分;数组有一个字段,可以通过访问。
反射提供了访问数组类型和数组组件类型、创建新数组以及检索和设置数组组件值的方法。以下各节包括对数组上常见操作的示例:
- 识别数组类型描述了如何确定类成员是否是数组类型的字段
- 创建新数组演示了如何创建具有简单和复杂组件类型的新数组实例
- 获取和设置数组及其组件展示了如何访问数组类型的字段以及单独访问数组元素
- 故障排除涵盖了常见错误和编程误解
所有这些操作都通过中的方法支持。
识别数组类型
原文:
可以通过调用来识别数组类型。要获取一个,请使用本教程中检索类对象部分描述的方法之一。
示例标识了命名类中的数组类型字段,并报告了每个字段的组件类型。
返回值的语法在中有描述。类型名称开头的’'字符的数量表示数组的维度(即嵌套的深度)。
输出示例如下。用户输入用斜体表示。一个原始类型为的数组:
一个引用类型为的数组:
是一个引用类型的一维数组,而是一个引用类型的二维数组:
创建新数组
原文:
就像非反射代码一样,反射支持通过动态创建任意类型和维度的数组的能力。考虑,一个能够动态创建数组的基本解释器。将解析的语法如下:
假设代表一个具有接受单个参数的构造函数的类。数组的维度由提供的值的数量确定。以下示例将构造一个数组的实例,并用、等给定的实例填充其值。(此示例假定熟悉和。有关的反射 API 的讨论,请参阅本教程的 Creating New Class Instances 部分。)
上面的示例展示了可能希望通过反射创建数组的一种情况;即如果组件类型直到运行时才知道。在这种情况下,代码使用获取所需组件类型的类,然后调用特定的构造函数来初始化数组的每个组件,然后设置相应的数组值。
获取和设置数组及其组件
原文:
就像在非反射代码中一样,可以整体设置或逐个组件设置或检索数组字段。要一次设置整个数组,请使用。要检索整个数组,请使用。可以使用中的方法来设置或检索单个组件。
提供了形式为和的方法,用于设置和获取任何原始类型的组件。例如,可以使用设置数组的组件,并可以使用检索它。
这些方法支持自动扩宽数据类型。因此,可以用于设置数组的值,因为一个 16 位的可以被扩宽为 32 位的而不会丢失数据;另一方面,在数组上调用将导致抛出,因为 64 位的不能被缩小为 32 位的而不丢失信息。无论传递的实际值是否能够准确表示为目标数据类型,这都是正确的。Java 语言规范,Java SE 7 版,章节Widening Primitive Conversion和Narrowing Primitive Conversion包含了对扩宽和缩窄转换的完整讨论。
引用类型数组(包括数组的数组)的组件使用和进行设置和检索。
设置类型为数组的字段
示例演示了如何替换类型为数组的字段的值。在这种情况下,代码将的后备数组替换为更大的数组。(这假设原始的创建在不可修改的代码中;否则,可以简单地使用接受输入缓冲区大小的替代构造函数。)
请注意,上述示例使用了数组实用方法。包含许多在操作数组时方便的方法。
访问多维数组的元素
多维数组简单来说就是嵌套数组。二维数组是数组的数组。三维数组是二维数组的数组,依此类推。示例演示了如何使用反射创建和初始化多维数组。
通过使用以下代码片段也可以获得相同的结果:
可变参数提供了一个方便的方式来创建多维数组,但组件仍然需要使用多维数组是嵌套数组的原则进行初始化。(反射不提供用于此目的的多个索引/方法。)
故障排除
原文:
以下示例展示了在操作数组时可能发生的典型错误。
由于不可转换的类型导致
示例将生成一个。调用来设置一个类型的组件,其值为基本类型。在非反射等效的中,编译器会将值转换(或装箱)为引用类型,以便其类型检查接受该语句。在使用反射时,类型检查仅在运行时发生,因此没有机会将值装箱。
要消除此异常,有问题的行应该被以下调用替换:
提示: 当使用反射设置或获取数组组件时,编译器无法执行装箱。它只能转换与规范描述的相关类型。该示例预计会失败,因为在此测试中将返回,可以用程序验证特定转换是否可能:
同样,在反射中从基本类型到引用类型的自动转换也是不可能的。
对空数组的
示例说明了如果尝试访问长度为零的数组元素将会发生的错误:
提示: 可以有没有元素的数组(空数组)。在常见代码中只有少数情况下会看到它们,但它们可能会在反射中无意中出现。当然,无法设置/获取空数组的值,因为会抛出。
如果尝试缩小范围会导致
示例包含的代码会失败,因为它尝试执行一个可能会丢失数据的操作:
提示: 和 方法将执行自动扩展转换,但如果尝试进行缩小转换,则会抛出 。有关扩展和缩小转换的完整讨论,请参阅Java 语言规范,Java SE 7 版,分别查看Widening Primitive Conversion和Narrowing Primitive Conversion部分。
枚举类型
原文:
枚举是一种语言构造,用于定义类型安全的枚举,当需要固定一组命名值时可以使用。所有枚举隐式扩展 。枚举可以包含一个或多个枚举常量,这些常量定义了枚举类型的唯一实例。枚举声明定义了一个枚举类型,与类非常相似,可以具有字段、方法和构造函数等成员(有一些限制)。
由于枚举是类,反射不需要定义一个显式的类。枚举特定的反射 API 只有 、 和 。涉及枚举的大多数反射操作与任何其他类或成员相同。例如,枚举常量被实现为枚举上的字段。以下部分展示了如何在枚举中使用 和 。
- 检查枚举 说明了如何检索枚举的常量以及任何其他字段、构造函数和方法
- 使用枚举类型获取和设置字段 展示了如何使用枚举常量值设置和获取字段
- 故障排除描述了与枚举相关的常见错误
有关枚举的介绍,请参阅 枚举类型 课程。
检查枚举
原文:
反射提供了三个特定于枚举的 API:
表示此类是否表示枚举类型
检索由枚举定义的枚举常量列表,按照它们声明的顺序
表示此字段是否表示枚举类型的元素
有时需要动态检索枚举常量的列表;在非反射代码中,可以通过在枚举上调用隐式声明的静态方法 来实现这一点。 如果枚举类型的实例不可用,则获取可能值列表的唯一方法是调用 ,因为无法实例化枚举类型。
给定完全限定名称, 示例显示如何使用 检索枚举中常量的有序列表。
输出示例如下。 用户输入以斜体显示。
该示例还表明,通过调用 返回的值与在枚举类型上调用 返回的值相同。
由于枚举是类,可以使用本教程中描述的字段、方法和构造函数部分中描述的相同反射 API 获取其他信息。 代码示例说明了如何使用这些 API 获取有关枚举声明的其他信息。 该示例使用 来限制要检查的类集。 它还使用 来区分枚举声明中的枚举常量和其他字段(并非所有字段都是枚举常量)。
输出显示,的声明仅包含三个枚举常量。枚举常量暴露为字段。字段、构造函数和方法是由编译器生成的。字段与方法的实现有关。
注意:出于各种原因,包括支持枚举类型的演变,枚举常量的声明顺序很重要。和不能保证返回值的顺序与声明源代码中的顺序匹配。如果应用程序需要排序,请使用。
对于的输出显示,更复杂的枚举是可能的。这个类包括几个方法以及额外声明为的字段,这些字段不是枚举常量。
使用枚举类型获取和设置字段
原文:
存储枚举的字段与任何其他引用类型一样设置和检索,使用和。有关访问字段的更多信息,请参阅本教程的 Fields 部分。
考虑一个需要在服务器应用程序中动态修改跟踪级别的应用程序,通常在运行时不允许此更改。假设服务器对象的实例可用。示例展示了代码如何将枚举的表示转换为枚举类型,并检索和设置存储枚举的字段的值。
由于枚举常量是单例,可以使用和运算符来比较相同类型的枚举常量。
故障排除
原文:
以下示例展示了在使用枚举类型时可能遇到的问题。
尝试实例化枚举类型时出现 IllegalArgumentException
正如前面提到的,实例化枚举类型是被禁止的。示例尝试这样做。
提示: 明确实例化枚举是一种编译时错误,因为这将阻止定义的枚举常量保持唯一。这种限制也在反射代码中执行。试图使用默认构造函数实例化类的代码应该首先调用来确定该类是否为枚举。
设置具有不兼容枚举类型的字段时出现 IllegalArgumentException
存储枚举的字段应该设置为适当的枚举类型。(实际上,任何类型的字段都必须设置为兼容的类型。)示例会产生预期的错误。
提示: 严格来说,将类型为的字段设置为类型为的值只有在以下语句成立时才能成功:
代码可以修改以执行以下测试,以验证类型是否兼容:
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/1909.html