反射概述
Java(TM)Core Reflection API 提供了一个小型的、类型安全的API,它支持对当前Java虚拟机*中的类和对象进行内省。如果安全策略允许,API可用于:
- 构造新的类实例和新数组
- 访问和修改对象和类的字段
- 对对象和类调用方法
- 访问和修改数组元素
核心反射API定义了类和方法,如下所示:
- 反射类、接口成员和构造函数的三个类字段、方法和构造函数。
- 有关基础成员或构造函数的反射信息
- 类型安全意味着使用成员或构造函数对Java对象进行操作
- 类类的方法,用于构造字段、方法和构造函数类的新实例。
- 类数组提供了动态构造和访问Java数组的方法。
- 实用程序类修饰符帮助解码关于类及其成员的Java语言修饰符信息。
- 类调用
TargetException
用于包装由反射的方法或构造函数引发的异常。 - 两个类
AccessibleObject
和ReflectPermission
提供了一种机制来禁止标准Java语言访问控制。
还有一些部分java.lang
语言支持反射的包。这些是:
- 保存类类实例的静态字段。它们表示运行时的基本Java类型
boolean
、byte
、char
、short
、int
、long
、float
和double
,以及关键字void
。 - 一个不可实例化的占位符类
Void
,用于保存对表示关键字Void的类对象的引用。
反射应用
核心反射API可容纳两类应用程序。
一类是由需要基于目标对象的运行时类来发现和使用所有公共成员的应用程序组成的。所有的应用程序和方法都需要一个公共的时间来运行。此类别中的示例包括 Java(TM)Beans 等服务和轻量级工具,如对象检查器。这些应用程序使用通过类类类的getField
、getMethod
、getConstructor
、getFields
、getMethods
和getConstructors
获得的类字段、方法和构造函数的实例。
第二类是复杂的应用程序,它们需要发现和使用给定类声明的成员。这些应用程序需要在类文件提供的级别上对类的实现进行运行时访问。此类别中的示例包括开发工具,如解释器、检查器和类浏览器,以及运行时服务,如 Java(TM)对象序列化。这些应用程序使用通过类类类的getDeclaredField
、getDeclaredMethod
、getDeclaredConstructor
、getDeclaredFields
、getDeclaredMethods
和getDeclaredConstructor
方法获得的类字段、方法和构造函数的实例。
反射模型
字段、方法和构造函数这三个类是最终的。只有Java虚拟机可以创建这些类的实例;这些对象用于操作底层对象;即:
- 获取有关基础成员或构造函数的反射信息
- 获取和设置字段值
- 对对象或类调用方法
- 创建类的新实例
最后一个不可实例化的类数组提供了静态方法,这些方法允许创建新的数组,以及获取和设置数组的元素。
成员接口
类字段、方法和构造函数实现成员接口。Member
方法用于查询反射成员的基本标识信息。标识信息包括声明成员的类或接口、成员本身的名称以及成员的Java语言修饰符(例如public
、protected
、abstract
、synchronized
等)。
字段对象
场对象表示反射场。基础字段可以是类变量(静态字段)或实例变量(非静态字段)。类字段的方法用于获取基础字段的类型,以及获取和设置对象的基础字段值。
方法对象
方法对象表示反射的方法。底层方法可以是抽象方法、实例方法或类(静态)方法。
类方法的方法用于获取基础方法的形式参数类型、返回类型和检查的异常类型。另外,类方法的invoke
方法用于调用目标对象上的底层方法。实例和抽象方法调用使用基于目标对象的运行时类和反射方法的声明类、名称和形式参数类型的动态方法解析。(因此,允许对实现接口的类的实例的对象调用反射接口方法。)静态方法调用使用方法声明类的底层静态方法。
构造函数对象
构造函数对象表示反射的构造函数。类构造函数的方法用于获取基础构造函数的形式参数类型和检查的异常类型。此外,类构造函数的newInstance
方法用于创建和初始化声明构造函数的类的新实例,前提是该类是可实例化的。
数组和修饰符类
Array
类是一个不可实例化的类,它导出类方法来创建具有基元或类组件类型的Java数组。数组类的方法也用于获取和设置数组组件值。
修饰符类是一个不可实例化的类,它导出类方法来解码类和成员的Java语言修饰符。语言修饰符以整数编码,并使用Java虚拟机规范定义的编码常量。
原始Java类型的表示
最后,有9个类对象用于表示8个基本Java类型和运行时的void
。(请注意,这些对象是类对象,而不是类。)核心反射API使用这些对象来标识以下对象:
- 基本字段类型
- 基本方法和构造函数参数类型
- 基元方法返回类型
Java虚拟机创建了这9个类对象。它们与它们所代表的类型具有相同的名称。只能通过以下公共最终静态变量引用类对象:
java.lang.Boolean.TYPE
java.lang.Character.TYPE
java.lang.Byte.TYPE
java.lang.Short.TYPE
java.lang.Integer.TYPE
java.lang.Long.TYPE
java.lang.Float.TYPE
java.lang.Double.TYPE
java.lang.Void.TYPE
特别是,这些类对象不能通过类Class
的forName
方法访问。
反射安全模型
Java安全管理器逐类控制对核心反射API的访问。有两个级别的检查来执行安全保障,如下所示:
- 类
Class
的方法提供对类的成员或一组成员的反射访问,是字段、方法和构造函数实例的唯一源。这些方法首先将安全检查委托给系统安全管理器(如果已安装),如果反射访问请求被拒绝,系统安全管理器将抛出SecurityException
。 - 一旦系统安全管理器向成员授予初始反射访问权限,任何代码都可以查询被反射成员的标识信息。但是,标准的Java语言访问控制检查受保护的、默认的(包)访问以及私有类和成员的访问通常会在单个反射成员用于操作对象的底层成员(即获取或设置字段值、调用方法或创建和初始化新对象)时进行。可以使用
setAccessible
方法将覆盖标准语言访问控制规则的无限制访问授予特权代码。此方法由类字段、方法和构造函数从AccessibleObject
继承。
初始策略决策集中在SecurityManager
类的两个方法中:
void checkMemberAccess(Class,int)
引发SecurityException
checkMemberAccess
的Class参数标识需要访问其成员的类或接口。int参数标识要访问的成员集会员。公众或者成员。声明.void checkPackageAccess(String pkg)
引发SecurityException
策略是根据授予调用方的权限来确定的。课堂上有两个动作java.lang.RuntimePermission
影响这些政策。这些是:
accessDeclaredMembers
。这就赋予了对非公开的班级成员进行反思的能力。accessClassInPackage{package name}
。这将授予对指定包中类的访问权。这些权限由安全管理器确定。
如果对指定类的指定成员集的请求访问被拒绝,则该方法应引发SecurityException
。如果请求的对集合的访问被授予,则该方法应返回。
如前所述,当使用此集合中的反射成员对底层对象进行操作时,通常会强制实施标准Java语言访问控制,即:
- 字段用于获取或设置字段值
- 方法用于调用方法
- 构造函数用于创建和初始化类的新实例
如果此时拒绝访问,则反射成员将抛出IllegalAccessException
。下面可以解释为使用特定的访问控制的Java方法来禁止访问标志。
Java语言策略
应用程序的Java语言安全策略是,任何代码都可以获得对它可能链接到的任何类的所有成员和构造函数(包括非公共成员和构造函数)的反射访问。默认情况下,获得对成员或构造函数的反射访问的应用程序代码只能使用具有标准Java语言访问控制的反射成员或构造函数。
可以通过调用反射成员的setAccessible
方法来重写标准策略。调用setAccessible
方法的能力又由permission ReflectPermission
的suppressAccessChecks
目标控制。
关于反射和内省的区别参考这篇文章:https://javakk.com/725.html
反射数据转换
反射包中的某些方法在基元类型的值和类类型的对象之间执行自动数据转换。这些是获取和设置字段和数组组件值的通用方法,以及方法和构造函数调用的方法。
有两种类型的自动数据转换。从基本类型的对象类型转换为类的值转换。展开转换将类类型的对象转换为基元类型的值。这些转换的规则在“包装和展开转换”中定义
此外,字段访问和方法调用允许扩展基元和引用类型的转换。
包装和取消包装转换
原始值在通过检索时自动包装在对象中字段。获取或者数组Array.get
,或当它由通过调用的方法返回时方法调用.
类似地,对象值在需要基元类型值的上下文中作为参数提供时会自动展开。这些上下文是:
- 字段集,其中基础字段具有基元类型
- 数组.set,其中基础数组具有基元元素类型
- 方法调用或者构造函数
Constructor.newInstance
,其中基础方法或构造函数的相应形式参数具有基元类型
下表显示了基元类型与类(包装器)类型之间的对应关系:
声明为void
的方法在通过调用时返回特殊引用null
方法调用.
加宽转换
反射包允许在运行时进行与编译时方法调用上下文中允许的相同的扩展转换。
在加宽时执行运行转换:
- 通过field和array方法从字段或数组检索值时
- 通过field和array方法将值存储到字段或数组中时
- 当在方法或构造函数调用期间,通过将未包装的实际参数值转换为其相应的形式参数的类型时方法调用
Method.invoke
或者构造函数Constructor.newInstance
允许的加宽基本体转换为:
- 从字节
byte
到short
、int
、long
、float
或double
- 从
short
到int
、long
、float
或double
- 或从长整型到双精度浮点型
- 从
int
到long
、float
或double
- 从长到浮或双倍
- 从浮动到双精度
允许的加宽参考转换为:
- 从类class类型S到类类型T,前提是S是T的子类
- 从类class类型S到接口类型K,前提是S实现K
- 从接口类型J到接口类型K,前提是J是K的子接口
除特别注明外,本站所有文章均为老K的Java博客原创,转载请注明出处来自https://javakk.com/739.html
暂无评论