- 封闭类:
MethodHandles
需要创建方法句柄的查找类将调用 MethodHandles.lookup
为自己创建一个工厂。创建 Lookup
工厂对象时,确定查找类的标识,并将其安全地存储在 Lookup
对象中。查找类(或其委托)然后可以使用 Lookup
对象上的工厂方法来为访问检查成员创建方法句柄。这包括查找类允许的所有方法、构造函数和字段,甚至是私有的。
查找工厂方法
Lookup
对象上的工厂方法对应于方法、构造函数和字段的所有主要用例。工厂方法创建的每个方法句柄都是特定 bytecode behavior 的功能等价物。 (字节码行为在 Java 虚拟机规范的 5.4.3.5 部分进行了描述。)以下是这些工厂方法与生成的方法句柄的行为之间的对应关系的摘要:
查找表达式 | 成员 | 字节码行为 |
---|---|---|
lookup.findGetter(C.class,"f",FT.class) |
FT f; |
(T) this.f; |
lookup.findStaticGetter(C.class,"f",FT.class) |
static FT f; |
(FT) C.f; |
lookup.findSetter(C.class,"f",FT.class) |
FT f; |
this.f = x; |
lookup.findStaticSetter(C.class,"f",FT.class) |
static FT f; |
C.f = arg; |
lookup.findVirtual(C.class,"m",MT) |
T m(A*); |
(T) this.m(arg*); |
lookup.findStatic(C.class,"m",MT) |
static T m(A*); |
(T) C.m(arg*); |
lookup.findSpecial(C.class,"m",MT,this.class) |
T m(A*); |
(T) super.m(arg*); |
lookup.findConstructor(C.class,MT) |
C(A*); |
new C(arg*); |
lookup.unreflectGetter(aField) |
(static )?FT f; |
(FT) aField.get(thisOrNull); |
lookup.unreflectSetter(aField) |
(static )?FT f; |
aField.set(thisOrNull, arg); |
lookup.unreflect(aMethod) |
(static )?T m(A*); |
(T) aMethod.invoke(thisOrNull, arg*); |
lookup.unreflectConstructor(aConstructor) |
C(A*); |
(C) aConstructor.newInstance(arg*); |
lookup.unreflectSpecial(aMethod,this.class) |
T m(A*); |
(T) super.m(arg*); |
lookup.findClass("C") |
class C { ... } |
C.class; |
C
是正在搜索成员的类或接口,在查找方法中记录为名为 refc
的参数。方法类型 MT
由返回类型 T
和参数类型序列 A*
组成。构造函数还具有一系列参数类型 A*
并被视为返回类型为 C
的新创建对象。 MT
和字段类型 FT
都记录为名为 type
的参数。形参this
代表C
类型的自引用;如果存在,它始终是方法句柄调用的前导参数。 (在某些 protected
成员的情况下,this
的类型可能仅限于查找类;见下文。)名称 arg
代表所有其他方法句柄参数。在 Core Reflection API 的代码示例中,如果访问的方法或字段是静态的,则名称 thisOrNull
代表空引用,否则代表 this
。名称 aMethod
、 aField
和 aConstructor
代表对应于类型 C
中声明的给定成员的反射对象。
findClass
操作的字节码行为是常量类的加载,就像 ldc CONSTANT_Class
一样。该行为不是表示为方法句柄,而是直接表示为 Class
常量。
在给定成员具有可变元数(即方法或构造函数)的情况下,返回的方法句柄也将为 变数 。在所有其他情况下,返回的方法句柄将具有固定的数量。
Discussion:查找的方法句柄和底层类成员以及字节码行为之间的等价性可以通过几种方式分解:
- 如果
C
不能从查找类的加载器中以符号方式访问,查找仍然可以成功,即使没有等效的 Java 表达式或字节码常量。 - 同样,如果
T
或MT
不能从查找类的加载器中以符号方式访问,查找仍然可以成功。例如,查找MethodHandle.invokeExact
和MethodHandle.invoke
将始终成功,无论请求的类型如何。 - 如果安装了安全管理器,它可以基于各种理由禁止查找 (见下文)。相比之下,
CONSTANT_MethodHandle
常量上的ldc
指令不受安全管理器检查。 - 如果查找的方法具有 非常大的数量 ,则方法句柄创建可能会失败并显示
IllegalArgumentException
,因为方法句柄类型具有 参数太多。
访问检查
创建方法句柄时,访问检查应用于Lookup
的工厂方法。这是与 Core Reflection API 的一个关键区别,因为 java.lang.reflect.Method.invoke
在每次调用时对每个调用者执行访问检查。
所有访问检查都从 Lookup
对象开始,该对象将其记录的查找类与创建方法句柄的所有请求进行比较。单个 Lookup
对象可用于创建任意数量的访问检查方法句柄,所有这些都针对单个查找类进行检查。
Lookup
对象可以与其他可信代码共享,例如元对象协议。共享的 Lookup
对象委托了在查找类的私有成员上创建方法句柄的能力。即使特权代码使用 Lookup
对象,访问检查也仅限于原始查找类的特权。
查找可能会失败,因为查找类无法访问包含类,或者因为缺少所需的类成员,或者因为查找类无法访问所需的类成员,或者因为查找对象不够可信访问该成员。对于 final
字段上的字段设置函数,最终执行被视为一种访问控制,查找将失败,但 Lookup.unreflectSetter
的特殊情况除外。在任何这些情况下,都会从尝试的查找中抛出 ReflectiveOperationException
。确切的类将是以下之一:
- NoSuchMethodException — 如果请求的方法不存在
- NoSuchFieldException — 如果一个字段被请求但不存在
- IllegalAccessException — 如果成员存在但访问检查失败
通常,可以为方法 M
查找方法句柄的条件并不比查找类可以编译、验证和解析对 M
的调用的条件更具限制性。在 JVM 会引发类似 NoSuchMethodError
的异常的地方,方法句柄查找通常会引发相应的已检查异常,例如 NoSuchMethodException
。调用查找产生的方法句柄的效果是 完全等同 执行对 M
的编译、验证和解析调用。字段和构造函数也是如此。
Discussion: 访问检查仅适用于命名和反射方法、构造函数和字段。其他方法句柄创建方法(例如 MethodHandle.asType
)不需要任何访问检查,并且独立于任何 Lookup
对象使用。
如果所需成员是 protected
,则适用通常的 JVM 规则,包括查找类必须与所需成员位于同一包中或必须继承该成员的要求。 (参见 Java 虚拟机规范,4.9.2、5.4.3.5 和 6.4 部分。)此外,如果所需成员是不同包中的非静态字段或方法,则生成的方法句柄可能仅应用于查找的对象类或其子类之一。通过将前导 this
参数的类型从 C
(必须是查找类的超类)缩小到查找类本身来强制执行此要求。
JVM 对 invokespecial
指令施加了类似的要求,即接收者参数必须与解析的方法 and 当前类匹配。同样,通过将前导参数的类型缩小为生成的方法句柄来强制执行此要求。 (请参阅 Java 虚拟机规范,4.10.1.9 部分。)
JVM 将构造函数和静态初始化程序块表示为具有特殊名称("<init>"
和 "<clinit>"
)的内部方法。调用指令的内部语法允许它们引用此类内部方法,就好像它们是普通方法一样,但 JVM 字节码验证器拒绝它们。查找此类内部方法将生成 NoSuchMethodException
。
如果嵌套类型之间的关系直接通过 NestHost
和 NestMembers
属性表示(参见 Java 虚拟机规范,4.7.28 和 4.7.29 部分),则关联的 Lookup
对象提供对查找类及其所有 nestmates 的直接访问(参见 Class.getNestHost
) .否则,嵌套类之间的访问由 Java 编译器创建包装器方法来访问同一嵌套中另一个类的私有方法。例如,嵌套类 C.D
可以访问其他相关类(如 C
、 C.D.E
或 C.B
)中的私有成员,但 Java 编译器可能需要在这些相关类中生成包装方法。在这种情况下,C.E
上的 Lookup
对象将无法访问这些私有成员。此限制的解决方法是 Lookup.in
方法,它可以将对 C.E
的查找转换为对任何其他类的查找,而无需特殊的特权提升。
根据其 lookupModes
的集合,允许对给定查找对象的访问可能仅限于查找类通常可访问的成员子集。例如,publicLookup
方法生成一个查找对象,该对象只允许访问导出包的公共类中的公共成员。调用者敏感方法 lookup
生成一个具有与其调用者类相关的全部功能的查找对象,以模拟所有支持的字节码行为。此外,Lookup.in
方法可能会生成比原始查找对象访问模式更少的查找对象。
Discussion of private and module access: 我们说查找有 private access 如果它的 查找模式 包括访问 private
成员(包括 nestmates 的私有成员)的可能性。正如其他地方的相关方法中所记录的那样,只有具有私有访问权限的查找才具有以下功能:
- 访问查找类及其 nestmates 的私有字段、方法和构造方法
- 创建方法处理哪些
emulate invokespecial
指令 - 避免包访问检查查找类可访问的类
- 创建
delegated lookup objects
,它可以私有访问同一包成员中的其他类
同样,具有模块访问权限的查找可确保原始查找创建者是与查找类相同的模块中的成员。
私有和模块访问是独立确定的模式;查找可能有一个或两个或两个都没有。拥有两种访问模式的查找被称为拥有 完全权限访问 。
使用 original access 进行查找可确保此查找是由原始查找类和 VM 调用的引导方法创建的。这种具有原始访问权限的查找也具有私有和模块访问权限,具有以下附加功能:
这些权限中的每一个都是以下事实的结果:具有私有访问权限的查找对象可以安全地追溯到原始类,其 字节码行为 和 Java 语言访问权限可以通过方法句柄可靠地确定和模拟。
跨模块查找
当一个模块M1
中的查找类访问另一个模块 M2
中的类时,会在访问模式位之外执行额外的访问检查。当 M2
对 M1
可读且类型位于 M2
的包中且至少导出到 M1
时,具有 PUBLIC
模式和 M1
中的查找类的 Lookup
可以访问 M2
中的公共类型。
C
上的 Lookup
也可以通过 Lookup.in
和 MethodHandles.privateLookupIn
方法 teleport 到目标类。跨模块传送将始终将原始查找类记录为 previous lookup class 并丢弃 MODULE
访问权限。如果目标类与查找类 C
在同一个模块中,则目标类成为新的查找类,并且与之前的查找类没有变化。如果目标类与 M1
(C
的模块)位于不同的模块中,则 C
成为新的先前查找类,目标类成为新的查找类。在这种情况下,如果 M0
中已经有一个先前的查找类,并且它不同于 M1
和 M2
,那么生成的查找将放弃所有特权。例如,
Lookup lookup = MethodHandles.lookup(); // in class C
Lookup lookup2 = lookup.in(D.class);
MethodHandle mh = lookup2.findStatic(E.class, "m", MT);
MethodHandles.lookup()
工厂方法生成一个带有 null
先前查找类的 Lookup
对象。 lookup.in(D.class)
将类 C
上的 lookup
转换为类 D
而无需提升权限。如果 C
和 D
在同一个模块中,lookup2
将 D
记录为新的查找类,并保留与原始 lookup
或 null
(如果不存在)相同的先前查找类。
当 Lookup
从一个巢穴中的一个类传送到另一个巢穴时,PRIVATE
访问权限将被删除。当 Lookup
从一个包中的类传送到另一个包时,PACKAGE
访问权限将被删除。当 Lookup
从一个模块中的类传送到另一个模块时,MODULE
访问权限将被丢弃。跨模块传送会降低在新查找类的模块和旧查找类的模块中访问非导出类的能力,并且生成的 Lookup
仍然只有 PUBLIC
访问权限。一个Lookup
可以来回传送到查找类的模块和上一个类查找的模块中的一个类。跨模块传送只能减少访问权限而不能增加访问权限。传送到某个第三个模块会丢弃所有访问权限。
在上面的示例中,如果 C
和 D
在不同的模块中,lookup2
将 D
记录为它的查找类,将 C
记录为其之前的查找类,而 lookup2
只有 PUBLIC
访问权限。 lookup2
可以传送到 C
模块和 D
模块中的其他类。如果类 E
在第三个模块中,则 lookup2.in(E.class)
在 E
上创建一个没有访问权限的 Lookup
并且 lookup2
的查找类 D
被记录为其先前的查找类。
跨模块传送限制了对查找类和前一个查找类可以平等访问的公共类型的访问(见下文)。
MethodHandles.privateLookupIn(T.class, lookup)
可用于将 lookup
从类 C
传送到类 T
并创建一个新的 Lookup
和 私人访问 如果允许查找类在 T
上执行 deep reflection。 lookup
必须具有 MODULE
和 PRIVATE
访问权限才能调用 privateLookupIn
。允许模块M1
中C
上的lookup
对M1
中的所有类进行深度反射。如果 T
在 M1
中,则 privateLookupIn
在 T
上生成具有全部功能的新 Lookup
。如果M1
读取M2
和M2
opens
包含T
的包至少M1
,C
上的lookup
也被允许对另一个模块M2
中的T
进行深度反射。 T
成为新的查找类,C
成为新的先前查找类,并且 MODULE
访问从生成的 Lookup
中删除。生成的 Lookup
可用于执行成员查找或通过调用 Lookup::in
传送到另一个查找类。但它不能用于通过调用privateLookupIn
来获取另一个私有Lookup
,因为它没有MODULE
访问权限。
跨模块访问检查
Lookup
with PUBLIC
或 with UNCONDITIONAL
模式允许跨模块访问。访问检查针对查找类和先前的查找类(如果存在)执行。
当类型位于 无条件出口 包中时,具有 UNCONDITIONAL
模式的 Lookup
可以访问所有模块中的公共类型。
如果 M1
中的 LC
上的 Lookup
没有以前的查找类,则使用 PUBLIC
模式的查找可以访问模块中的所有公共类型,这些类型对 M1
是可读的,并且该类型位于至少导出到 M1
的包中。
如果 M1
中的 LC
上的 Lookup
在 M0
上具有先前的查找类 PLC
,则使用 PUBLIC
模式的查找可以访问 M1
可访问的所有公共类型与 M0
可访问的所有公共类型的交集。 M0
读取 M1
因此可访问类型集包括:
- 来自
M1
的无条件导出包 - 如果
M1
读取M0
,则从M0
无条件导出包 - 如果
M0
和M1
都读取M2
,则从第三个模块M2
无条件导出包 - 从
M1
到M0
限定导出的包 - 如果
M1
读取M0
,则从M0
到M1
的合格出口包 - 从第三个模块
M2
到M0
和M1
的合格导出包,如果M0
和M1
都读取M2
访问模式
下表显示了由以下任何工厂或转换方法生成的Lookup
的访问模式:
MethodHandles::lookup
MethodHandles::publicLookup
MethodHandles::privateLookupIn
Lookup::in
Lookup::dropLookupMode
查找对象 | 原来的 | protected | 私人的 | 包 | 模块 | public |
---|---|---|---|---|---|---|
CL = MethodHandles.lookup() 在 C |
ORI | PRO | PRI | PAC | MOD | 1R |
CL.in(C1) 相同的包 |
PAC | MOD | 1R | |||
CL.in(C1) 相同的模块 |
MOD | 1R | ||||
CL.in(D) 不同的模块 |
2R | |||||
CL.in(D).in(C) 跳回模块 |
2R | |||||
PRI1 = privateLookupIn(C1,CL) |
PRO | PRI | PAC | MOD | 1R | |
PRI1a = privateLookupIn(C,PRI1) |
PRO | PRI | PAC | MOD | 1R | |
PRI1.in(C1) 相同的包 |
PAC | MOD | 1R | |||
PRI1.in(C1) 不同的包 |
MOD | 1R | ||||
PRI1.in(D) 不同的模块 |
2R | |||||
PRI1.dropLookupMode(PROTECTED) |
PRI | PAC | MOD | 1R | ||
PRI1.dropLookupMode(PRIVATE) |
PAC | MOD | 1R | |||
PRI1.dropLookupMode(PACKAGE) |
MOD | 1R | ||||
PRI1.dropLookupMode(MODULE) |
1R | |||||
PRI1.dropLookupMode(PUBLIC) |
没有任何 | |||||
PRI2 = privateLookupIn(D,CL) |
PRO | PRI | PAC | 2R | ||
privateLookupIn(D,PRI1) |
PRO | PRI | PAC | 2R | ||
privateLookupIn(C,PRI2) 失败 |
IAE | |||||
PRI2.in(D2) 相同的包 |
PAC | 2R | ||||
PRI2.in(D2) 不同的包 |
2R | |||||
PRI2.in(C1) 跳回模块 |
2R | |||||
PRI2.in(E) 跳到第三个模块 |
没有任何 | |||||
PRI2.dropLookupMode(PROTECTED) |
PRI | PAC | 2R | |||
PRI2.dropLookupMode(PRIVATE) |
PAC | 2R | ||||
PRI2.dropLookupMode(PACKAGE) |
2R | |||||
PRI2.dropLookupMode(MODULE) |
2R | |||||
PRI2.dropLookupMode(PUBLIC) |
没有任何 | |||||
CL.dropLookupMode(PROTECTED) |
PRI | PAC | MOD | 1R | ||
CL.dropLookupMode(PRIVATE) |
PAC | MOD | 1R | |||
CL.dropLookupMode(PACKAGE) |
MOD | 1R | ||||
CL.dropLookupMode(MODULE) |
1R | |||||
CL.dropLookupMode(PUBLIC) |
没有任何 | |||||
PUB = publicLookup() |
U | |||||
PUB.in(D) 不同的模块 |
U | |||||
PUB.in(D).in(E) 第三模块 |
U | |||||
PUB.dropLookupMode(UNCONDITIONAL) |
没有任何 | |||||
privateLookupIn(C1,PUB) 失败 |
IAE | |||||
ANY.in(X) ,用于无法访问的 X |
没有任何 |
笔记:
- 类
C
和类C1
在模块M1
中,但D
和D2
在模块M2
中,E
在模块M3
中。X
代表查找无法访问的类。ANY
代表任何示例查找。 ORI
表示ORIGINAL
位设置,PRO
表示PROTECTED
位设置,PRI
表示PRIVATE
位设置,PAC
表示PACKAGE
位设置,MOD
表示MODULE
位设置,_ n140804_ 和2R
表示PUBLIC
位设置,U
表示UNCONDITIONAL
位设置,IAE
表示IllegalAccessException
抛出。- 公共访问分为三种:
- 无条件(
U
):查找假定可读性。查找具有null
先前的查找类。 - one-module-reads (
1R
):模块访问检查是针对查找类执行的。查找具有null
先前的查找类。 - two-module-reads (
2R
):针对查找类和前一个查找类执行模块访问检查。查找有一个非空的先前查找类,它与当前查找类位于不同的模块中。
- 无条件(
- 任何试图到达第三个模块的尝试都会失去所有访问权限。
- 如果
Lookup::in
无法访问目标类X
,则所有访问模式都将被删除。
安全管理器交互
尽管字节码指令只能引用相关类加载器中的类,但此 API 可以搜索任何类中的方法,只要对其Class
对象的引用可用即可。这种交叉加载器引用也可以通过核心反射 API 实现,并且不可能对 invokestatic
或 getfield
等字节码指令进行编码。有一个 安全管理器API 允许应用程序检查此类交叉加载程序引用。这些检查适用于 MethodHandles.Lookup
API 和核心反射 API(在 Class
上找到)。
如果存在安全管理器,则成员和类查找会受到额外检查。安全经理接到了一到三个电话。这些调用中的任何一个都可以通过抛出 SecurityException
来拒绝访问。将 smgr
定义为安全管理器,将 lookc
定义为当前查找对象的查找类,将 refc
定义为查找成员的包含类,将 defc
定义为实际定义成员的类。 (如果正在访问类或其他类型,则 refc
和 defc
值是类本身。)如果当前查找对象没有 完全权限访问,则值 lookc
定义为 not present。调用是根据以下规则进行的:
- 步骤1:如果
lookc
不存在,或者如果它的类加载器与refc
的类加载器不同或不是其祖先,则调用smgr.checkPackageAccess(refcPkg)
,其中refcPkg
是refc
的包。 - 步骤 2a:如果检索到的成员不是公开的并且
lookc
不存在,则调用smgr.checkPermission
和RuntimePermission("accessDeclaredMembers")
。 - 步骤 2b:如果检索到的类具有
null
类加载器,并且lookc
不存在,则调用smgr.checkPermission
和RuntimePermission("getClassLoader")
。 - 第 3 步:如果检索到的成员不是公开的,并且
lookc
不存在,并且defc
和refc
不同,则调用smgr.checkPackageAccess(defcPkg)
,其中defcPkg
是defc
的包。
如果存在安全管理器并且当前查找对象没有 完全权限访问,则 defineClass
、defineHiddenClass
、defineHiddenClassWithClassData
调用 smgr.checkPermission
和 RuntimePermission("defineClass")
。
调用者敏感方法
少数 Java 方法具有称为调用者敏感性的特殊属性。 caller-sensitive 方法的行为可能会有所不同,具体取决于其直接调用者的身份。如果请求调用者敏感方法的方法句柄,则适用 字节码行为 的一般规则,但它们以特殊方式考虑查找类。生成的方法句柄的行为就好像它是从查找类中包含的指令调用的一样,以便调用者敏感的方法检测到查找类。 (相比之下,方法句柄的调用者被忽略。)因此,在调用者敏感方法的情况下,不同的查找类可能会产生不同行为的方法句柄。
在查找对象为 publicLookup()
或不带 原始访问 的其他查找对象的情况下,将忽略查找类。在这种情况下,无法创建对调用者敏感的方法句柄,禁止访问,并且查找失败并返回 IllegalAccessException
。
Discussion: 例如,调用者敏感方法 Class.forName(x)
可以返回不同的类或抛出不同的异常,具体取决于调用它的类的类加载器。 Class.forName
的公共查找将失败,因为没有合理的方法来确定其字节码行为。
如果应用程序缓存方法句柄以进行广泛共享,则应使用 publicLookup()
来创建它们。如果查找 Class.forName
,它将失败,应用程序必须在这种情况下采取适当的措施。可能是稍后的查找,也许在调用引导方法期间,可以包含调用者的特定身份,从而使该方法可访问。
MethodHandles.lookup
函数对调用者敏感,因此可以为查找奠定安全的基础。 JSR 292 API 中的几乎所有其他方法都依赖查找对象来检查访问请求。
-
内部类总结
内部类修饰符和类型类描述static enum
一组类选项,指定由Lookup::defineHiddenClass
方法创建的隐藏类是否作为新成员动态添加到查找类的嵌套和/或隐藏类是否与标记为其定义加载器的类加载器有很强的关系。 -
字段摘要
字段修饰符和类型Field描述static final int
表示module
访问的单位掩码,可能有助于lookupModes
的结果。static final int
表示original
访问的单位掩码可能有助于lookupModes
的结果。static final int
表示package
访问(默认访问)的单位掩码,这可能有助于lookupModes
的结果。static final int
表示private
访问的单位掩码,可能有助于lookupModes
的结果。static final int
表示protected
访问的单位掩码,可能有助于lookupModes
的结果。static final int
表示public
访问的单位掩码,可能有助于lookupModes
的结果。static final int
代表unconditional
访问的单位掩码可能有助于lookupModes
的结果。 -
方法总结
修饰符和类型方法描述Class<?>
accessClass
(Class<?> targetClass) 确定是否可以从此Lookup
对象定义的查找上下文访问类。bind
(Object receiver, String name, MethodType type) 为非静态方法生成早期绑定方法句柄。Class<?>
defineClass
(byte[] bytes) defineHiddenClass
(byte[] bytes, boolean initialize, MethodHandles.Lookup.ClassOption... options) 从bytes
创建一个 hidden 类或接口,在新创建的类或接口上返回一个Lookup
。defineHiddenClassWithClassData
(byte[] bytes, Object classData, boolean initialize, MethodHandles.Lookup.ClassOption... options) dropLookupMode
(int modeToDrop) 在此查找对象找到成员的同一查找类上创建一个查找,但查找模式已丢失给定的查找模式。Class<?>
ensureInitialized
(Class<?> targetClass) 确保targetClass
已被初始化。Class<?>
findConstructor
(Class<?> refc, MethodType type) 使用指定类型的构造方法生成一个方法句柄,该句柄创建一个对象并对其进行初始化。findGetter
(Class<?> refc, String name, Class<?> type) 生成一个方法句柄,提供对非静态字段的读取访问权限。findSetter
(Class<?> refc, String name, Class<?> type) 生成一个方法句柄,提供对非静态字段的写访问权限。findSpecial
(Class<?> refc, String name, MethodType type, Class<?> specialCaller) 为虚拟方法生成早期绑定方法句柄。findStatic
(Class<?> refc, String name, MethodType type) 为静态方法生成方法句柄。findStaticGetter
(Class<?> refc, String name, Class<?> type) 生成一个方法句柄,提供对静态字段的读取访问权限。findStaticSetter
(Class<?> refc, String name, Class<?> type) 生成一个方法句柄,提供对静态字段的写访问权。findStaticVarHandle
(Class<?> decl, String name, Class<?> type) 生成一个 VarHandle,允许访问类型为decl
的类中声明的类型为type
的静态字段name
。findVarHandle
(Class<?> recv, String name, Class<?> type) 生成一个 VarHandle,允许访问类型为recv
的类中声明的类型为type
的非静态字段name
。findVirtual
(Class<?> refc, String name, MethodType type) 为虚拟方法生成方法句柄。boolean
如果此查找具有 full privilege access,则返回true
,即boolean
已弃用。在指定的新查找类上创建查找。Class<?>
告诉哪个类正在执行查找。int
告知此查找对象可以生成哪些成员访问保护类。Class<?>
报告另一个模块中的查找类,该查找对象先前是从中传送的,或null
。revealDirect
(MethodHandle target) 破解由此查找对象或类似对象创建的 直接方法句柄。toString()
显示要从中进行查找的类的名称,后跟“/”和 上一个查找类 的名称(如果存在)。制作一个 直接方法句柄 到m,如果查找类具有权限。为反射的构造方法生成方法句柄。生成一个方法句柄,提供对反射字段的读取访问权限。生成一个方法句柄,提供对反射字段的写访问权限。unreflectSpecial
(Method m, Class<?> specialCaller) 为反射方法生成方法句柄。生成一个 VarHandle,可以访问类型为R
的类中声明的类型为T
的反射字段f
。
-
字段详细信息
-
PUBLIC
public static final int PUBLIC- 参见:
-
PRIVATE
public static final int PRIVATE- 参见:
-
PROTECTED
public static final int PROTECTED- 参见:
-
PACKAGE
public static final int PACKAGE- 参见:
-
MODULE
public static final int MODULE表示module
访问的单位掩码,可能有助于lookupModes
的结果。该值为0x10
,与任何特定的 修改位 没有任何意义对应。结合PUBLIC
修饰符位,具有此查找模式的Lookup
可以访问查找类模块中的所有公共类型以及其他模块导出到查找类模块的包中的公共类型。如果设置了此查找模式,则 上一个查找类 始终为
null
。- 自从:
- 9
- 参见:
-
UNCONDITIONAL
public static final int UNCONDITIONAL表示unconditional
访问的单位掩码可能有助于lookupModes
的结果。该值为0x20
,与任何特定的 修改位 没有任何意义对应。具有此查找模式的Lookup
假定为 可读性。当类型位于exported unconditionally
包中时,此查找模式可以访问所有模块的公共类型的所有公共成员。如果设置了此查找模式,则 上一个查找类 始终为
null
。- 自从:
- 9
- 参见:
-
ORIGINAL
public static final int ORIGINAL表示original
访问的单位掩码可能有助于lookupModes
的结果。该值为0x40
,与任何特定的 修改位 没有任何意义对应。如果设置了此查找模式,
Lookup
对象必须由原始查找类通过调用MethodHandles.lookup()
方法或由 VM 调用的引导程序方法创建。具有此查找模式的Lookup
对象具有 完全权限访问。- 自从:
- 16
- 参见:
-
-
方法详情
-
lookupClass
告诉哪个类正在执行查找。正是针对此类执行可见性和访问权限检查。如果此查找对象具有 上一个查找类 ,则会针对查找类和前一个查找类执行访问检查。
该类意味着最高级别的访问权限,但权限可能会受到位掩码
lookupModes
的额外限制,它控制是否可以访问非公共成员。- 返回:
- 查找类,此查找对象代表它查找成员
- 参见:
-
previousLookupClass
报告另一个模块中的查找类,该查找对象先前是从中传送的,或null
。由工厂方法(例如
lookup()
和publicLookup()
方法)生成的Lookup
对象具有null
先前的查找类。当此查找从一个模块中的旧查找类传送到另一个模块中的新查找类时,Lookup
对象具有非空的先前查找类。- 返回:
-
此查找对象先前从中传送的另一个模块中的查找类,或
null
- 自从:
- 14
- 参见:
-
lookupModes
public int lookupModes()告知此查找对象可以生成哪些成员访问保护类。结果是 PUBLIC (0x01)、PRIVATE (0x02)、PROTECTED (0x04)、PACKAGE (0x08)、MODULE (0x10)、UNCONDITIONAL (0x20) 和 ORIGINAL (0x40) 位的位掩码。调用者类 上新创建的查找对象设置了所有可能的位,
UNCONDITIONAL
除外。新查找类 从以前的查找对象创建 上的查找对象可能会将某些模式位设置为零。模式位也可以是 直接清除 。一旦清除,模式位就无法从降级的查找对象中恢复。这样做的目的是限制通过新查找对象的访问,以便它只能访问原始查找对象以及新查找类可以访问的名称。- 返回:
- 查找模式,它限制了此查找对象执行的访问类型
- 参见:
-
in
在指定的新查找类上创建查找。结果对象会将指定的类报告为它自己的lookupClass
。但是,生成的
Lookup
对象保证不具有比原始对象更多的访问能力。特别是,访问能力可能会丢失如下:- 如果新查找类与旧查找类不同,即
ORIGINAL
访问将丢失。 - 如果新查找类位于与旧查找类不同的模块中,即
MODULE
访问将丢失。 - 如果新查找类位于与旧查找类不同的包中,则将无法访问受保护和默认(包)成员,即
PROTECTED
和PACKAGE
访问权丢失。 - 如果新查找类与旧查找类不在同一个包成员中,则私有成员将不可访问,受保护成员将无法通过继承访问,即
PRIVATE
访问丢失。 (由于包共享,受保护的成员可能会继续访问。) - 如果新的查找类不是此查找的无障碍,则没有成员,甚至公共成员都不能访问,即所有访问模式都将丢失。
- 如果新查找类、旧查找类和先前查找类都在不同的模块中,即传送到第三个模块,则所有访问模式都将丢失。
新的先前查找类选择如下:
- 如果新的查找对象有
UNCONDITIONAL
位,则新的先前查找类是null
。 - 如果新的查找类与旧的查找类在同一个模块中,则新的先前查找类是旧的先前查找类。
- 如果新查找类与旧查找类位于不同的模块中,则新的先前查找类是旧查找类。
生成的查找加载类的能力(在
findClass(java.lang.String)
调用期间使用)由查找类的加载器决定,它可能会因该操作而改变。- 参数:
requestedLookupClass
- 新查找对象所需的查找类- 返回:
- 报告所需查找类的查找对象,如果没有更改,则为同一对象
- 抛出:
IllegalArgumentException
- 如果requestedLookupClass
是原始类型或 void 或数组类NullPointerException
- 如果参数为空- 参见:
- 如果新查找类与旧查找类不同,即
-
dropLookupMode
在此查找对象找到成员的同一查找类上创建一个查找,但查找模式已丢失给定的查找模式。要删除的查找模式是PUBLIC
、MODULE
、PACKAGE
、PROTECTED
、PRIVATE
、ORIGINAL
或UNCONDITIONAL
之一。如果此查找是 公开查询 ,则此查找已设置
UNCONDITIONAL
模式且未设置其他模式。当在公共查找中删除UNCONDITIONAL
时,结果查找将无权访问。如果此查找不是公共查找,则无论其 查找模式 为何,以下内容均适用。
PROTECTED
和ORIGINAL
始终被删除,因此生成的查找模式永远不会具有这些访问功能。当删除PACKAGE
时,结果查找将没有PACKAGE
或PRIVATE
访问权限。当删除MODULE
时,结果查找将没有MODULE
、PACKAGE
或PRIVATE
访问权限。当删除PUBLIC
时,结果查找将无权访问。- API 注意:
-
使用
PACKAGE
但不是PRIVATE
模式的查找可以安全地委托查找类包内的非公共访问,而无需授予 私人访问 。使用MODULE
但不是PACKAGE
模式的查找可以安全地委托查找类模块内的PUBLIC
访问,而无需授予包访问权限。使用 上一个查找类(和PUBLIC
但不是MODULE
模式)的查找可以安全地委托对查找类的模块和前一个查找类的模块都可以访问的公共类的访问。 - 参数:
modeToDrop
- 要删除的查找模式- 返回:
- 缺少指示模式的查找对象,或者如果没有更改则为相同的对象
- 抛出:
IllegalArgumentException
- 如果modeToDrop
不是PUBLIC
、MODULE
、PACKAGE
、PROTECTED
、PRIVATE
、ORIGINAL
或UNCONDITIONAL
之一- 自从:
- 9
- 参见:
-
defineClass
使用相同的类加载器和相同的运行时包和 保护域 创建并链接来自bytes
的类或接口作为此查找的 查找类 ,就好像调用ClassLoader::defineClass
一样。此查找的 查找模式 必须包括
PACKAGE
访问,因为默认(包)成员将可供类访问。PACKAGE
查找模式用于验证查找对象是否由运行时包中的调用者创建(或派生自最初由具有适当特权的代码创建到运行时包中的目标类的查找)。bytes
参数是有效类文件(由 The Java Virtual Machine Specification 定义)的类字节,类名与查找类位于同一包中。此方法不运行类初始值设定项。类初始值设定项可能会在稍后运行,详见 The Java Language Specification 的第 12.4 节。
如果有安全管理器并且此查找没有 完全权限访问 ,则首先调用其
checkPermission
方法来检查RuntimePermission("defineClass")
。- 参数:
bytes
- 类字节- 返回:
-
类的
Class
对象 - 抛出:
IllegalAccessException
- 如果此查找没有PACKAGE
访问权限ClassFormatError
- 如果bytes
不是ClassFile
结构IllegalArgumentException
- 如果bytes
表示与查找类不同的包中的类,或者bytes
不是类或接口(ACC_MODULE
标志在access_flags
项的值中设置)VerifyError
- 如果无法验证新创建的类LinkageError
- 如果由于任何其他原因无法链接新创建的类SecurityException
- 如果存在安全管理器并且它 拒绝访问NullPointerException
- 如果bytes
是null
- 自从:
- 9
- 参见:
-
defineHiddenClass
public MethodHandles.Lookup defineHiddenClass(byte[] bytes, boolean initialize, MethodHandles.Lookup.ClassOption ... options) throws IllegalAccessException 从bytes
创建一个 hidden 类或接口,在新创建的类或接口上返回一个Lookup
。通常,类或接口
C
由类加载器创建,类加载器直接定义C
或委托给另一个类加载器。类加载器通过调用ClassLoader::defineClass
直接定义C
,这会导致 Java 虚拟机从class
文件格式的声称表示派生C
。在不希望使用类加载器的情况下,可以通过此方法创建类或接口C
。此方法能够定义C
,从而创建它,而无需调用ClassLoader::defineClass
。相反,此方法定义C
就好像通过安排 Java 虚拟机使用以下规则从class
文件格式的声称表示派生非数组类或接口C
一样:- 此
Lookup
的 查找模式 必须包含 完全特权 访问权限。需要此访问级别才能在此Lookup
的查找类的模块中创建C
。 bytes
中声称的表示必须是受支持的主要和次要版本的ClassFile
结构(JVMS 4.1)。主要版本和次要版本可能与此Lookup
的查找类的class
文件版本不同。this_class
的值必须是constant_pool
表中的有效索引,并且该索引处的条目必须是有效的CONSTANT_Class_info
结构。设N
为该结构指定的以内部形式编码的二进制名称。N
必须表示与查找类位于同一包中的类或接口。- 让
CN
成为字符串N + "." + <suffix>
,其中<suffix>
是一个非限定名称。设
newBytes
为bytes
给出的ClassFile
结构,并在constant_pool
表中添加了一个条目,指示CN
的CONSTANT_Utf8_info
结构,其中this_class
指示的CONSTANT_Class_info
结构指的是新的CONSTANT_Utf8_info
结构。让
L
成为这个Lookup
的查找类的定义类加载器。C
的派生名称为CN
,类加载器为L
,声称的表示形式为newBytes
,就像 JVMS 5.3.5 的规则一样,并进行了以下调整:this_class
指示的常量允许指定包含单个"."
字符的名称,即使这不是内部形式的有效二进制类或接口名称。- Java 虚拟机将
L
标记为C
的定义类加载器,但没有类加载器被记录为C
的初始类加载器。 C
被认为具有与此Lookup
的查找类相同的运行时 包、module 和 保护域。- 设
GN
是通过采用N
(以内部形式编码的二进制名称)并将 ASCII 正斜杠替换为 ASCII 句点而获得的二进制名称。对于代表C
的Class
实例:Class.getName()
返回字符串GN + "/" + <suffix>
,即使这不是有效的二进制类或接口名称。Class.descriptorString()
返回字符串"L" + N + "." + <suffix> + ";"
,即使这不是有效的类型描述符名称。Class.describeConstable()
返回一个空的可选值,因为C
不能在 名义形式 中描述。
C
派生后,由 Java 虚拟机链接。链接按照 JVMS 5.4.3 中的规定进行,并进行了以下调整:- 在验证期间,每当需要加载名为
CN
的类时,尝试都会成功,生成类C
。没有请求任何类加载器。 - 在任何尝试解析
this_class
指示的运行时常量池中的条目时,符号引用被认为已解析为C
并且解析总是立即成功。
如果
initialize
参数为true
,则C
由 Java 虚拟机初始化。新创建的类或接口
C
充当此方法返回的Lookup
对象的 查找类。C
是 hidden,因为没有其他类或接口可以通过常量池条目引用C
。也就是说,隐藏类或接口不能被任何其他类命名为超类型、字段类型、方法参数类型或方法返回类型。这是因为隐藏类或接口没有二进制名称,因此在任何类的常量池中都没有可用的内部形式来记录。Class.forName(String, boolean, ClassLoader)
、ClassLoader.loadClass(String, boolean)
或findClass(String)
无法发现隐藏类或接口,Java 代理或使用 JVM 工具接口 的工具代理也无法发现隐藏类或接口。类加载器 创建的类或接口与该类加载器有很强的关系。也就是说,每个
Class
对象都包含对ClassLoader
和 定义它 的引用。这意味着当且仅当它的定义加载器不可访问时,由类加载器创建的类才可以被卸载,因此可以被垃圾收集器回收 (JLS 12.7)。但是,默认情况下,即使标记为定义加载器的类加载器是 可达的 ,也可以卸载隐藏类或接口。当隐藏类或接口服务于任意类加载器定义的多个类时,此行为很有用。在其他情况下,隐藏类或接口可以链接到具有与隐藏类或接口相同的定义加载器的单个类(或少量类)。在这种情况下,隐藏类或接口必须与普通类或接口相连,可以在options
中传递STRONG
选项。这安排隐藏类与标记为其定义加载器的类加载器具有相同的强关系,就像普通类或接口与其自己的定义加载器一样。如果未使用STRONG
,则defineHiddenClass
的调用者仍可通过确保Class
对象可访问来阻止隐藏类或接口被卸载。卸载特性在定义时为每个隐藏类设置,以后不能更改。允许独立于标记为定义加载器的类加载器卸载隐藏类的一个优点是应用程序可能会创建大量隐藏类。相反,如果使用
STRONG
,那么 JVM 可能会耗尽内存,就好像普通类是由类加载器创建的一样。允许嵌套中的类和接口相互访问它们的私有成员。嵌套关系由
class
文件中的NestHost
属性 (JVMS 4.7.28 ) 和NestMembers
属性 (JVMS 4.7.29 ) 确定。默认情况下,隐藏类属于仅由其自身组成的嵌套,因为隐藏类没有二进制名称。NESTMATE
选项可以传入options
以创建隐藏类或接口C
作为嵌套成员。C
所属的嵌套不基于派生C
的ClassFile
结构中的任何NestHost
属性。相反,以下规则确定C
的嵌套主机:- 如果之前已经确定了这个
Lookup
的查找类的嵌套宿主,那么让H
成为查找类的嵌套宿主。否则,使用 JVMS 中的算法确定查找类的嵌套主机 5.4.4 ,产生H
。 C
的嵌套主机被确定为H
,查找类的嵌套主机。
隐藏的类或接口可能是可序列化的,但这需要自定义序列化机制以确保实例被正确序列化和反序列化。默认序列化机制仅支持可通过类名发现的类和接口。
- 参数:
bytes
- 构成类数据的字节,采用由定义的有效class
文件的格式Java 虚拟机规范.initialize
- 如果true
类将被初始化。options
- 类选项- 返回:
-
隐藏类上的
Lookup
对象,具有 原来的 和 完全特权 访问权限 - 抛出:
IllegalAccessException
- 如果这个Lookup
没有 完全特权 访问权限SecurityException
- 如果存在安全管理器并且它 拒绝访问ClassFormatError
- 如果bytes
不是ClassFile
结构UnsupportedClassVersionError
- 如果bytes
不是受支持的主要或次要版本IllegalArgumentException
- 如果bytes
表示与查找类不同的包中的类,或者bytes
不是类或接口(ACC_MODULE
标志在access_flags
项的值中设置)IncompatibleClassChangeError
- 如果命名为C
的直接超类的类或接口实际上是一个接口,或者如果命名为C
的直接超类的任何类或接口实际上不是接口ClassCircularityError
- 如果C
的任何超类或超接口是C
本身VerifyError
- 如果无法验证新创建的类LinkageError
- 如果由于任何其他原因无法链接新创建的类NullPointerException
- 如果任何参数是null
- 看Java 语言规范:
-
12.7 类和接口的卸载
- 看Java 虚拟机规范:
-
4.2.1 二进制类和接口名称
4.2.2 非限定名称
4.7.28NestHost
属性
4.7.29NestMembers
属性
5.4.3.1 类和接口解析
5.4.4 访问控制
5.3.5 从类
文件表示中导出Class
5.4 链接
5.5 初始化
- 自从:
- 15
- 参见:
- 此
-
defineHiddenClassWithClassData
public MethodHandles.Lookup defineHiddenClassWithClassData(byte[] bytes, Object classData, boolean initialize, MethodHandles.Lookup.ClassOption ... options) throws IllegalAccessException 从bytes
和关联的 类数据 创建一个 hidden 类或接口,在新创建的类或接口上返回一个Lookup
。这个方法相当于调用
defineHiddenClass(bytes, initialize, options)
就好像隐藏类注入了private static final无名在类初始化程序的第一条指令中使用给定的classData
初始化的字段。新创建的类由 Java 虚拟机链接。MethodHandles::classData
和MethodHandles::classDataAt
方法可用于检索classData
。- API 注意:
-
框架可以创建一个隐藏类,其中包含一个或多个对象的类数据,并通过引导方法将类数据加载为动态计算的常量。
Class data
只能由新定义的隐藏类创建的查找对象访问,但同一嵌套中的其他成员无法访问(与 nestmates 可访问的私有静态字段不同)。应该注意可变性,例如在通过类数据传递数组或其他可变结构时。在运行时更改存储在类数据中的任何值可能会导致不可预知的行为。如果类数据是List
,最好使其不可修改,例如通过List::of
。 - 参数:
bytes
- 类字节classData
- 预初始化类数据initialize
- 如果true
类将被初始化。options
- 类选项- 返回:
-
隐藏类上的
Lookup
对象,具有 原来的 和 完全特权 访问权限 - 抛出:
IllegalAccessException
- 如果这个Lookup
没有 完全特权 访问权限SecurityException
- 如果存在安全管理器并且它 拒绝访问ClassFormatError
- 如果bytes
不是ClassFile
结构UnsupportedClassVersionError
- 如果bytes
不是受支持的主要或次要版本IllegalArgumentException
- 如果bytes
表示与查找类不同的包中的类,或者bytes
不是类或接口(ACC_MODULE
标志在access_flags
项的值中设置)IncompatibleClassChangeError
- 如果命名为C
的直接超类的类或接口实际上是一个接口,或者如果命名为C
的直接超类的任何类或接口实际上不是接口ClassCircularityError
- 如果C
的任何超类或超接口是C
本身VerifyError
- 如果无法验证新创建的类LinkageError
- 如果由于任何其他原因无法链接新创建的类NullPointerException
- 如果任何参数是null
- 看Java 语言规范:
-
12.7 类和接口的卸载
- 看Java 虚拟机规范:
-
4.2.1 二进制类和接口名称
4.2.2 非限定名称
4.7.28NestHost
属性
4.7.29NestMembers
属性
5.4.3.1 类和接口解析
5.4.4 访问控制
5.3.5 从类
文件表示中导出Class
5.4 链接
5.5 初始化
- 自从:
- 16
- 参见:
-
toString
显示要从中进行查找的类的名称,后跟“/”和 上一个查找类 的名称(如果存在)。 (该名称是由Class.getName
报告的名称。)如果对该查找允许的访问有限制,则通过向类名添加一个后缀来表示,该后缀由一个斜杠和一个关键字组成。关键字代表最强的允许访问,选择如下:- 如果不允许访问,则后缀为“/noaccess”。
- 如果只允许无条件访问,则后缀为“/publicLookup”。
- 如果仅允许公开访问导出包中的类型,则后缀为“/public”。
- 如果只允许公共和模块访问,则后缀为“/module”。
- 如果允许公共和包访问,则后缀为“/package”。
- 如果允许 public、package 和 private 访问,则后缀为“/private”。
MethodHandles.lookup
获得的对象。Lookup.in
创建的对象始终具有受限访问权限,并将显示后缀。(protected access 应该比 private access 更强,这似乎很奇怪。独立于 package access 来看,protected access 是最先丢失的,因为它需要调用者和被调用者之间有直接的子类关系。)
-
findStatic
public MethodHandle findStatic(Class <?> refc, String name, MethodType type) throws NoSuchMethodException , IllegalAccessException 为静态方法生成方法句柄。方法句柄的类型将是方法的类型。 (由于静态方法不接受接收器,因此没有额外的接收器参数插入到方法句柄类型中,就像findVirtual
或findSpecial
那样。)查找对象必须可以访问该方法及其所有参数类型。当且仅当设置了方法的可变元数修饰符位 (
0x0080
) 时,返回的方法句柄将具有 变数。如果返回的方法句柄被调用,方法的类将被初始化,如果它还没有被初始化的话。
示例:
import static java.lang.invoke.MethodHandles.*; import static java.lang.invoke.MethodType.*; ... MethodHandle MH_asList = publicLookup().findStatic(Arrays.class, "asList", methodType(List.class, Object[].class)); assertEquals("[x, y]", MH_asList.invoke("x", "y").toString());
- 参数:
refc
- 访问该方法的类name
- 方法的名称type
- 方法的类型- 返回:
- 所需的方法句柄
- 抛出:
NoSuchMethodException
- 如果该方法不存在IllegalAccessException
- 如果访问检查失败,或者如果方法不是static
,或者如果设置了方法的变量 arity 修饰符位并且asVarargsCollector
失败SecurityException
- 如果存在安全管理器并且它 拒绝访问NullPointerException
- 如果任何参数为空
-
findVirtual
public MethodHandle findVirtual(Class <?> refc, String name, MethodType type) throws NoSuchMethodException , IllegalAccessException 为虚拟方法生成方法句柄。方法句柄的类型将是方法的类型,并在前面加上接收者类型(通常是refc
)。查找对象必须可以访问该方法及其所有参数类型。调用时,句柄会将第一个参数视为接收者,并且对于非私有方法,将根据接收者的类型进行分派以确定进入哪个方法实现。对于私有方法,
refc
中的命名方法将在接收者上调用。 (调度操作与invokevirtual
或invokeinterface
指令执行的操作相同。)如果查找类具有访问该成员的全部权限,则第一个参数的类型为
refc
。否则该成员必须是protected
并且第一个参数的类型将被限制为查找类。当且仅当设置了方法的可变元数修饰符位 (
0x0080
) 时,返回的方法句柄将具有 变数。由于
invokevirtual
指令和findVirtual
生成的方法句柄之间的通用 等价,如果类为MethodHandle
并且名称字符串为invokeExact
或invoke
,则生成的方法句柄等同于由MethodHandles.exactInvoker
或MethodHandles.invoker
生成的具有相同 _n607 的方法句柄8_ 论点。如果类是
VarHandle
并且名称字符串对应于签名多态访问模式方法的名称,则生成的方法句柄等效于MethodHandles.varHandleInvoker(java.lang.invoke.VarHandle.AccessMode, java.lang.invoke.MethodType)
生成的方法句柄,其访问模式对应于名称字符串并具有相同的type
参数。示例:
import static java.lang.invoke.MethodHandles.*; import static java.lang.invoke.MethodType.*; ... MethodHandle MH_concat = publicLookup().findVirtual(String.class, "concat", methodType(String.class, String.class)); MethodHandle MH_hashCode = publicLookup().findVirtual(Object.class, "hashCode", methodType(int.class)); MethodHandle MH_hashCode_String = publicLookup().findVirtual(String.class, "hashCode", methodType(int.class)); assertEquals("xy", (String) MH_concat.invokeExact("x", "y")); assertEquals("xy".hashCode(), (int) MH_hashCode.invokeExact((Object)"xy")); assertEquals("xy".hashCode(), (int) MH_hashCode_String.invokeExact("xy")); // interface method: MethodHandle MH_subSequence = publicLookup().findVirtual(CharSequence.class, "subSequence", methodType(CharSequence.class, int.class, int.class)); assertEquals("def", MH_subSequence.invoke("abcdefghi", 3, 6).toString()); // constructor "internal method" must be accessed differently: MethodType MT_newString = methodType(void.class); //()V for new String() try { assertEquals("impossible", lookup() .findVirtual(String.class, "<init>", MT_newString)); } catch (NoSuchMethodException ex) { } // OK MethodHandle MH_newString = publicLookup() .findConstructor(String.class, MT_newString); assertEquals("", (String) MH_newString.invokeExact());
- 参数:
refc
- 访问方法的类或接口name
- 方法的名称type
- 方法的类型,省略了接收者参数- 返回:
- 所需的方法句柄
- 抛出:
NoSuchMethodException
- 如果该方法不存在IllegalAccessException
- 如果访问检查失败,或者如果方法是static
,或者如果设置了方法的变量 arity 修饰符位并且asVarargsCollector
失败SecurityException
- 如果存在安全管理器并且它 拒绝访问NullPointerException
- 如果任何参数为空
-
findConstructor
public MethodHandle findConstructor(Class <?> refc, MethodType type) throws NoSuchMethodException , IllegalAccessException 使用指定类型的构造方法生成一个方法句柄,该句柄创建一个对象并对其进行初始化。方法句柄的参数类型将是构造方法的参数类型,而返回类型将是对构造方法类的引用。查找对象必须可以访问构造方法及其所有参数类型。请求的类型必须具有
void
的返回类型。 (这与 JVM 对构造函数类型描述符的处理方式一致。)当且仅当构造函数的可变元数修饰符位 (
0x0080
) 已设置时,返回的方法句柄将具有 变数。如果返回的方法句柄被调用,构造方法的类将被初始化,如果它还没有被初始化的话。
示例:
import static java.lang.invoke.MethodHandles.*; import static java.lang.invoke.MethodType.*; ... MethodHandle MH_newArrayList = publicLookup().findConstructor( ArrayList.class, methodType(void.class, Collection.class)); Collection orig = Arrays.asList("x", "y"); Collection copy = (ArrayList) MH_newArrayList.invokeExact(orig); assert(orig != copy); assertEquals(orig, copy); // a variable-arity constructor: MethodHandle MH_newProcessBuilder = publicLookup().findConstructor( ProcessBuilder.class, methodType(void.class, String[].class)); ProcessBuilder pb = (ProcessBuilder) MH_newProcessBuilder.invoke("x", "y", "z"); assertEquals("[x, y, z]", pb.command().toString());
- 参数:
refc
- 访问方法的类或接口type
- 方法的类型,省略了接收者参数,返回类型为 void- 返回:
- 所需的方法句柄
- 抛出:
NoSuchMethodException
- 如果构造函数不存在IllegalAccessException
- 如果访问检查失败或者如果设置了方法的变量 arity 修饰符位并且asVarargsCollector
失败SecurityException
- 如果存在安全管理器并且它 拒绝访问NullPointerException
- 如果任何参数为空
-
findClass
public Class <?> findClass(String targetName) throws ClassNotFoundException , IllegalAccessException - 参数:
targetName
- 要查找的类的完全限定名称。- 返回:
- 请求的类。
- 抛出:
SecurityException
- 如果存在安全管理器并且它 拒绝访问LinkageError
- 如果链接失败ClassNotFoundException
- 如果查找类的加载器无法加载该类。IllegalAccessException
- 如果该类不可访问,则使用允许的访问模式。NullPointerException
- 如果targetName
为空- 看Java 虚拟机规范:
-
5.4.3.1 类和接口解析
- 自从:
- 9
-
ensureInitialized
确保targetClass
已被初始化。要初始化的类必须是无障碍到这个Lookup
对象。如果targetClass
尚未按照 JVMS 5.5 中的规定进行初始化,则此方法会导致对其进行初始化。当
targetClass
完全初始化时,或者当targetClass
正在被当前线程初始化时,此方法返回。- 参数:
targetClass
- 要初始化的类- 返回:
targetClass
已经初始化,或者正在被当前线程初始化。- 抛出:
IllegalArgumentException
- 如果targetClass
是原始类型或void
或数组类IllegalAccessException
- 如果targetClass
不是此查找的 无障碍ExceptionInInitializerError
- 如果此方法引发的类初始化失败SecurityException
- 如果存在安全管理器并且它 拒绝访问- 看Java 虚拟机规范:
-
5.5 初始化
- 自从:
- 15
-
accessClass
确定是否可以从此Lookup
对象定义的查找上下文访问类。类的静态初始值设定项未运行。如果targetClass
是数组类,则如果数组类的元素类型是可访问的,则targetClass
是可访问的。否则,targetClass
被确定为可访问,如下所示。如果
targetClass
与查找类位于同一模块中,则查找类是模块M1
中的LC
并且前一个查找类位于模块M0
或null
中(如果不存在),当且仅当以下其中一项为真时,targetClass
才可访问:- 如果此查找具有
PRIVATE
访问权限,则targetClass
是LC
或LC
同一嵌套中的其他类。 - 如果此查找具有
PACKAGE
访问权限,则targetClass
位于LC
的同一运行时包中。 - 如果此查找具有
MODULE
访问权限,则targetClass
是M1
中的公共类型。 - 如果此查找具有
PUBLIC
访问权限,则targetClass
是包中的公共类型,由M1
导出到至少M0
(如果存在前一个查找类);否则,targetClass
是M1
无条件导出的包中的公共类型。
否则,如果此查找具有
UNCONDITIONAL
访问权限,则当类型位于无条件导出的包中时,此查找可以访问所有模块中的公共类型。否则,
targetClass
与lookupClass
在不同的模块中,如果此查找没有PUBLIC
访问权限,则lookupClass
不可访问。否则,如果此查找没有 上一个查找类,
M1
是包含lookupClass
的模块,M2
是包含targetClass
的模块,那么targetClass
是可访问的当且仅当M1
读取M2
,并且targetClass
是公开的,并且在M2
至少导出到M1
的包中。
否则,如果此查找有一个 上一个查找类 ,
M1
和M2
和以前一样,并且M0
是包含前一个查找类的模块,那么targetClass
是可访问的当且仅当以下条件之一为真:targetClass
在M0
和M1
读取M0
并且类型在至少导出到M1
的包中。targetClass
在M1
和M0
读取M1
并且类型在至少导出到M0
的包中。targetClass
在第三个模块M2
中,M0
和M1
都读取M2
并且该类型位于至少导出到M0
和M2
的包中。
否则,
targetClass
不可访问。- 参数:
targetClass
- 要进行访问检查的类- 返回:
- 已经过访问检查的类
- 抛出:
IllegalAccessException
- 如果无法使用允许的访问模式从查找类和先前的查找类(如果存在)访问该类。SecurityException
- 如果存在安全管理器并且它 拒绝访问NullPointerException
- 如果targetClass
是null
- 自从:
- 9
- 参见:
- 如果此查找具有
-
findSpecial
public MethodHandle findSpecial(Class <?> refc, String name, MethodType type, Class <?> specialCaller) throws NoSuchMethodException , IllegalAccessException 为虚拟方法生成早期绑定方法句柄。它将绕过对接收方覆盖方法的检查,好像被称为 来自显式指定的specialCaller
中的invokespecial
指令。方法句柄的类型将是方法的类型,并在前面加上适当限制的接收器类型。 (接收者类型将为specialCaller
或子类型。)查找对象必须可以访问该方法及其所有参数类型。在方法解析之前,如果显式指定的调用者类与查找类不相同,或者如果此查找对象不具有私人访问权限,则访问失败。
当且仅当设置了方法的可变元数修饰符位 (
0x0080
) 时,返回的方法句柄将具有 变数。(Note: JVM internal methods named
"<init>"
are not visible to this API, even though theinvokespecial
instruction can refer to them in special circumstances. UsefindConstructor
to access instance initialization methods in a safe manner.)示例:
import static java.lang.invoke.MethodHandles.*; import static java.lang.invoke.MethodType.*; ... static class Listie extends ArrayList { public String toString() { return "[wee Listie]"; } static Lookup lookup() { return MethodHandles.lookup(); } } ... // no access to constructor via invokeSpecial: MethodHandle MH_newListie = Listie.lookup() .findConstructor(Listie.class, methodType(void.class)); Listie l = (Listie) MH_newListie.invokeExact(); try { assertEquals("impossible", Listie.lookup().findSpecial( Listie.class, "<init>", methodType(void.class), Listie.class)); } catch (NoSuchMethodException ex) { } // OK // access to super and self methods via invokeSpecial: MethodHandle MH_super = Listie.lookup().findSpecial( ArrayList.class, "toString" , methodType(String.class), Listie.class); MethodHandle MH_this = Listie.lookup().findSpecial( Listie.class, "toString" , methodType(String.class), Listie.class); MethodHandle MH_duper = Listie.lookup().findSpecial( Object.class, "toString" , methodType(String.class), Listie.class); assertEquals("[]", (String) MH_super.invokeExact(l)); assertEquals(""+l, (String) MH_this.invokeExact(l)); assertEquals("[]", (String) MH_duper.invokeExact(l)); // ArrayList method try { assertEquals("inaccessible", Listie.lookup().findSpecial( String.class, "toString", methodType(String.class), Listie.class)); } catch (IllegalAccessException ex) { } // OK Listie subl = new Listie() { public String toString() { return "[subclass]"; } }; assertEquals(""+l, (String) MH_this.invokeExact(subl)); // Listie method
- 参数:
refc
- 访问方法的类或接口name
- 方法的名称(不能是“<init>”)type
- 方法的类型,省略了接收者参数specialCaller
- 建议的调用类来执行invokespecial
- 返回:
- 所需的方法句柄
- 抛出:
NoSuchMethodException
- 如果该方法不存在IllegalAccessException
- 如果访问检查失败,或者如果方法是static
,或者如果设置了方法的变量 arity 修饰符位并且asVarargsCollector
失败SecurityException
- 如果存在安全管理器并且它 拒绝访问NullPointerException
- 如果任何参数为空
-
findGetter
public MethodHandle findGetter(Class <?> refc, String name, Class <?> type) throws NoSuchFieldException , IllegalAccessException 生成一个方法句柄,提供对非静态字段的读取访问权限。方法句柄的类型将具有字段值类型的返回类型。方法句柄的单个参数将是包含该字段的实例。代表查找类立即执行访问检查。- 参数:
refc
- 访问方法的类或接口name
- 字段名称type
- 字段的类型- 返回:
- 可以从字段加载值的方法句柄
- 抛出:
NoSuchFieldException
- 如果字段不存在IllegalAccessException
- 如果访问检查失败,或者字段为static
SecurityException
- 如果存在安全管理器并且它 拒绝访问NullPointerException
- 如果任何参数为空- 参见:
-
findSetter
public MethodHandle findSetter(Class <?> refc, String name, Class <?> type) throws NoSuchFieldException , IllegalAccessException 生成一个方法句柄,提供对非静态字段的写访问权限。方法句柄的类型将具有 void 返回类型。方法句柄将采用两个参数,包含字段的实例和要存储的值。第二个参数将是字段的值类型。代表查找类立即执行访问检查。- 参数:
refc
- 访问方法的类或接口name
- 字段名称type
- 字段的类型- 返回:
- 可以将值存储到字段中的方法句柄
- 抛出:
NoSuchFieldException
- 如果字段不存在IllegalAccessException
- 如果访问检查失败,或者字段为static
或final
SecurityException
- 如果存在安全管理器并且它 拒绝访问NullPointerException
- 如果任何参数为空- 参见:
-
findVarHandle
public VarHandle findVarHandle(Class <?> recv, String name, Class <?> type) throws NoSuchFieldException , IllegalAccessException 生成一个 VarHandle,允许访问类型为recv
的类中声明的类型为type
的非静态字段name
。 VarHandle 的变量类型是type
,它有一个坐标类型,recv
。代表查找类立即执行访问检查。
在以下情况下,不支持返回的 VarHandle 的某些访问模式:
- 如果该字段声明为
final
,则不支持写入、原子更新、数字原子更新和按位原子更新访问模式。 - 如果字段类型不是
byte
、short
、char
、int
、long
、float
或double
,则不支持数字原子更新访问模式。 - 如果字段类型不是
boolean
、byte
、short
、char
、int
或long
,则不支持按位原子更新访问模式。
如果该字段声明为
volatile
,则返回的 VarHandle 将根据其指定的访问模式覆盖对该字段的访问(有效地忽略volatile
声明)。如果字段类型是
float
或double
,则数字和原子更新访问模式使用它们的按位表示比较值(分别参见Float.floatToRawIntBits(float)
和Double.doubleToRawLongBits(double)
)。- API 注意:
float
值或double
值的按位比较,由数字和原子更新访问模式执行,不同于原始==
运算符以及Float.equals(java.lang.Object)
和Double.equals(java.lang.Object)
方法,特别是比较 NaN 值或比较-0.0
与+0.0
。在使用此类值执行比较和设置或比较和交换操作时应小心,因为操作可能会意外失败。有许多可能的 NaN 值在 Java 中被认为是NaN
,尽管 Java 提供的 IEEE 754 浮点运算无法区分它们。如果预期或见证值是 NaN 值并且它被转换(可能以特定于平台的方式)为另一个 NaN 值,并且因此具有不同的按位表示(请参阅Float.intBitsToFloat(int)
或Double.longBitsToDouble(long)
了解更多详细信息),则可能会发生操作失败。值-0.0
和+0.0
具有不同的按位表示,但在使用原始==
运算符时被视为相等。例如,如果数字算法计算的预期值为-0.0
并且先前计算的见证值为+0.0
,则可能会发生操作失败。- 参数:
recv
- 类型为R
的接收器类,它声明了非静态字段name
- 字段名称type
- 字段类型,类型为T
- 返回:
- 允许访问非静态字段的 VarHandle。
- 抛出:
NoSuchFieldException
- 如果字段不存在IllegalAccessException
- 如果访问检查失败,或者字段为static
SecurityException
- 如果存在安全管理器并且它 拒绝访问NullPointerException
- 如果任何参数为空- 自从:
- 9
- 如果该字段声明为
-
findStaticGetter
public MethodHandle findStaticGetter(Class <?> refc, String name, Class <?> type) throws NoSuchFieldException , IllegalAccessException 生成一个方法句柄,提供对静态字段的读取访问权限。方法句柄的类型将具有字段值类型的返回类型。方法句柄将不带任何参数。代表查找类立即执行访问检查。如果调用返回的方法句柄,则字段的类将被初始化(如果尚未初始化)。
- 参数:
refc
- 访问方法的类或接口name
- 字段名称type
- 字段的类型- 返回:
- 可以从字段加载值的方法句柄
- 抛出:
NoSuchFieldException
- 如果字段不存在IllegalAccessException
- 如果访问检查失败,或者字段不是static
SecurityException
- 如果存在安全管理器并且它 拒绝访问NullPointerException
- 如果任何参数为空
-
findStaticSetter
public MethodHandle findStaticSetter(Class <?> refc, String name, Class <?> type) throws NoSuchFieldException , IllegalAccessException 生成一个方法句柄,提供对静态字段的写访问权。方法句柄的类型将具有 void 返回类型。方法句柄将采用字段值类型的单个参数,即要存储的值。代表查找类立即执行访问检查。如果调用返回的方法句柄,则字段的类将被初始化(如果尚未初始化)。
- 参数:
refc
- 访问方法的类或接口name
- 字段名称type
- 字段的类型- 返回:
- 可以将值存储到字段中的方法句柄
- 抛出:
NoSuchFieldException
- 如果字段不存在IllegalAccessException
- 如果访问检查失败,或者字段不是static
或final
SecurityException
- 如果存在安全管理器并且它 拒绝访问NullPointerException
- 如果任何参数为空
-
findStaticVarHandle
public VarHandle findStaticVarHandle(Class <?> decl, String name, Class <?> type) throws NoSuchFieldException , IllegalAccessException 生成一个 VarHandle,允许访问类型为decl
的类中声明的类型为type
的静态字段name
。 VarHandle 的变量类型是type
,没有坐标类型。代表查找类立即执行访问检查。
如果对返回的 VarHandle 进行操作,则声明类将被初始化(如果尚未初始化)。
在以下情况下,不支持返回的 VarHandle 的某些访问模式:
- 如果该字段声明为
final
,则不支持写入、原子更新、数字原子更新和按位原子更新访问模式。 - 如果字段类型不是
byte
、short
、char
、int
、long
、float
或double
,则不支持数字原子更新访问模式。 - 如果字段类型不是
boolean
、byte
、short
、char
、int
或long
,则不支持按位原子更新访问模式。
如果该字段声明为
volatile
,则返回的 VarHandle 将根据其指定的访问模式覆盖对该字段的访问(有效地忽略volatile
声明)。如果字段类型是
float
或double
,则数字和原子更新访问模式使用它们的按位表示比较值(分别参见Float.floatToRawIntBits(float)
和Double.doubleToRawLongBits(double)
)。- API 注意:
float
值或double
值的按位比较,由数字和原子更新访问模式执行,不同于原始==
运算符以及Float.equals(java.lang.Object)
和Double.equals(java.lang.Object)
方法,特别是比较 NaN 值或比较-0.0
与+0.0
。在使用此类值执行比较和设置或比较和交换操作时应小心,因为操作可能会意外失败。有许多可能的 NaN 值在 Java 中被认为是NaN
,尽管 Java 提供的 IEEE 754 浮点运算无法区分它们。如果预期或见证值是 NaN 值并且它被转换(可能以特定于平台的方式)为另一个 NaN 值,并且因此具有不同的按位表示(请参阅Float.intBitsToFloat(int)
或Double.longBitsToDouble(long)
了解更多详细信息),则可能会发生操作失败。值-0.0
和+0.0
具有不同的按位表示,但在使用原始==
运算符时被视为相等。例如,如果数字算法计算的预期值为-0.0
并且先前计算的见证值为+0.0
,则可能会发生操作失败。- 参数:
decl
- 声明静态字段的类name
- 字段名称type
- 字段类型,类型为T
- 返回:
- 允许访问静态字段的 VarHandle
- 抛出:
NoSuchFieldException
- 如果字段不存在IllegalAccessException
- 如果访问检查失败,或者字段不是static
SecurityException
- 如果存在安全管理器并且它 拒绝访问NullPointerException
- 如果任何参数为空- 自从:
- 9
- 如果该字段声明为
-
bind
public MethodHandle bind(Object receiver, String name, MethodType type) throws NoSuchMethodException , IllegalAccessException 为非静态方法生成早期绑定方法句柄。接收者必须有一个超类型defc
,其中给定名称和类型的方法可供查找类访问。查找对象必须可以访问该方法及其所有参数类型。方法句柄的类型将是方法的类型,无需插入任何额外的接收者参数。给定的接收器将绑定到方法句柄中,因此每次调用方法句柄都会调用给定接收器上请求的方法。返回的方法句柄将具有 变数 当且仅当方法的变量 arity 修饰符位 (
0x0080
) 被设置时 and 尾随数组参数不是唯一的参数。 (如果尾随数组参数是唯一的参数,则给定的接收者值将绑定到它。)这几乎等同于以下代码,但有一些区别如下:
import static java.lang.invoke.MethodHandles.*; import static java.lang.invoke.MethodType.*; ... MethodHandle mh0 = lookup().findVirtual(defc, name, type); MethodHandle mh1 = mh0.bindTo(receiver); mh1 = mh1.withVarargs(mh0.isVarargsCollector()); return mh1;
defc
是receiver.getClass()
或该类的超类型,其中查找类可以访问请求的方法。 (与bind
不同,bindTo
不保留变量元数。此外,bindTo
可能会在bind
会抛出IllegalAccessException
的情况下抛出ClassCastException
,就像成员是protected
并且接收者被findVirtual
限制到查找类的情况一样。)- 参数:
receiver
- 访问该方法的对象name
- 方法的名称type
- 方法的类型,省略了接收者参数- 返回:
- 所需的方法句柄
- 抛出:
NoSuchMethodException
- 如果该方法不存在IllegalAccessException
- 如果访问检查失败或者如果设置了方法的变量 arity 修饰符位并且asVarargsCollector
失败SecurityException
- 如果存在安全管理器并且它 拒绝访问NullPointerException
- 如果任何参数为空- 参见:
-
unreflect
制作一个 直接方法句柄 到m,如果查找类具有权限。如果m是非静态的,接收者参数被视为初始参数。如果m是虚拟的,每次调用都尊重重写。与 Core Reflection API 不同,异常是 not 包装的。方法句柄的类型将是方法的类型,并在前面加上接收者类型(但前提是它是非静态的)。如果未设置方法的accessible
标志,则代表查找类立即执行访问检查。如果m不公开,请勿与不受信任的方共享生成的句柄。当且仅当设置了方法的可变元数修饰符位 (
0x0080
) 时,返回的方法句柄将具有 变数。如果m是静态的,如果返回的方法句柄被调用,方法的类将被初始化,如果它还没有被初始化的话。
- 参数:
m
- 反射方法- 返回:
- 可以调用反射方法的方法句柄
- 抛出:
IllegalAccessException
- 如果访问检查失败或者如果设置了方法的变量 arity 修饰符位并且asVarargsCollector
失败NullPointerException
- 如果参数为空
-
unreflectSpecial
public MethodHandle unreflectSpecial(Method m, Class <?> specialCaller) throws IllegalAccessException 为反射方法生成方法句柄。它将绕过对接收方覆盖方法的检查,好像被称为 来自显式指定的specialCaller
中的invokespecial
指令。方法句柄的类型将是方法的类型,并在前面加上适当限制的接收器类型。 (接收者类型将为specialCaller
或子类型。)如果未设置方法的accessible
标志,则代表查找类立即执行访问检查,就好像invokespecial
指令被链接一样。在方法解析之前,如果显式指定的调用者类与查找类不相同,或者如果此查找对象不具有私人访问权限,则访问失败。
当且仅当设置了方法的可变元数修饰符位 (
0x0080
) 时,返回的方法句柄将具有 变数。- 参数:
m
- 反射方法specialCaller
- 名义上调用方法的类- 返回:
- 可以调用反射方法的方法句柄
- 抛出:
IllegalAccessException
- 如果访问检查失败,或者如果方法是static
,或者如果设置了方法的变量 arity 修饰符位并且asVarargsCollector
失败NullPointerException
- 如果任何参数为空
-
unreflectConstructor
为反射的构造函数生成方法句柄。方法句柄的类型将是构造函数的类型,返回类型更改为声明类。方法句柄将执行newInstance
操作,在传递给方法句柄的参数上创建构造函数类的新实例。如果未设置构造函数的
accessible
标志,则立即代表查找类执行访问检查。当且仅当构造函数的可变元数修饰符位 (
0x0080
) 已设置时,返回的方法句柄将具有 变数。如果返回的方法句柄被调用,构造方法的类将被初始化,如果它还没有被初始化的话。
- 参数:
c
- 反射构造函数- 返回:
- 可以调用反射构造方法的方法句柄
- 抛出:
IllegalAccessException
- 如果访问检查失败或者如果设置了方法的变量 arity 修饰符位并且asVarargsCollector
失败NullPointerException
- 如果参数为空
-
unreflectGetter
生成一个方法句柄,提供对反射字段的读取访问权限。方法句柄的类型将具有字段值类型的返回类型。如果该字段是static
,则方法句柄将不带任何参数。否则,它的单个参数将是包含该字段的实例。如果未设置Field
对象的accessible
标志,则代表查找类立即执行访问检查。如果该字段是静态的,并且调用了返回的方法句柄,则该字段的类将被初始化(如果尚未初始化)。
- 参数:
f
- 反射场- 返回:
- 可以从反射字段加载值的方法句柄
- 抛出:
IllegalAccessException
- 如果访问检查失败NullPointerException
- 如果参数为空
-
unreflectSetter
生成一个方法句柄,提供对反射字段的写访问权限。方法句柄的类型将具有 void 返回类型。如果字段是static
,方法句柄将采用字段值类型的单个参数,即要存储的值。否则,这两个参数将是包含字段的实例和要存储的值。如果未设置Field
对象的accessible
标志,则代表查找类立即执行访问检查。如果该字段是
final
,将不允许写访问并且访问检查将失败,除非在为Field.set
记录的某些狭窄情况下。仅当对Field
对象的set
方法的相应调用可以正常返回时,才会返回方法句柄。特别是,同时为static
和final
的字段可能永远不会被设置。如果该字段是
static
,并且调用了返回的方法句柄,则该字段的类将被初始化(如果尚未初始化)。- 参数:
f
- 反射场- 返回:
- 可以将值存储到反射字段中的方法句柄
- 抛出:
IllegalAccessException
- 如果访问检查失败,或者如果字段为final
并且未在Field
对象上启用写访问NullPointerException
- 如果参数为空
-
unreflectVarHandle
生成一个 VarHandle,可以访问类型为R
的类中声明的类型为T
的反射字段f
。 VarHandle 的变量类型是T
。如果该字段是非静态的,则 VarHandle 具有一种坐标类型R
。否则,该字段是静态的,并且 VarHandle 没有坐标类型。代表查找类立即执行访问检查,而不管字段的
accessible
标志的值如何。如果该字段是静态的,并且如果返回的 VarHandle 被操作,则该字段的声明类将被初始化(如果它尚未初始化)。
在以下情况下,不支持返回的 VarHandle 的某些访问模式:
- 如果该字段声明为
final
,则不支持写入、原子更新、数字原子更新和按位原子更新访问模式。 - 如果字段类型不是
byte
、short
、char
、int
、long
、float
或double
,则不支持数字原子更新访问模式。 - 如果字段类型不是
boolean
、byte
、short
、char
、int
或long
,则不支持按位原子更新访问模式。
如果该字段声明为
volatile
,则返回的 VarHandle 将根据其指定的访问模式覆盖对该字段的访问(有效地忽略volatile
声明)。如果字段类型是
float
或double
,则数字和原子更新访问模式使用它们的按位表示比较值(分别参见Float.floatToRawIntBits(float)
和Double.doubleToRawLongBits(double)
)。- API 注意:
float
值或double
值的按位比较,由数字和原子更新访问模式执行,不同于原始==
运算符以及Float.equals(java.lang.Object)
和Double.equals(java.lang.Object)
方法,特别是比较 NaN 值或比较-0.0
与+0.0
。在使用此类值执行比较和设置或比较和交换操作时应小心,因为操作可能会意外失败。有许多可能的 NaN 值在 Java 中被认为是NaN
,尽管 Java 提供的 IEEE 754 浮点运算无法区分它们。如果预期或见证值是 NaN 值并且它被转换(可能以特定于平台的方式)为另一个 NaN 值,并且因此具有不同的按位表示(请参阅Float.intBitsToFloat(int)
或Double.longBitsToDouble(long)
了解更多详细信息),则可能会发生操作失败。值-0.0
和+0.0
具有不同的按位表示,但在使用原始==
运算符时被视为相等。例如,如果数字算法计算的预期值为-0.0
并且先前计算的见证值为+0.0
,则可能会发生操作失败。- 参数:
f
- 反射字段,具有T
类型的字段和R
类型的声明类- 返回:
- 允许访问非静态字段或静态字段的 VarHandle
- 抛出:
IllegalAccessException
- 如果访问检查失败NullPointerException
- 如果参数为空- 自从:
- 9
- 如果该字段声明为
-
revealDirect
破解由此查找对象或类似对象创建的 直接方法句柄。执行安全和访问检查以确保此查找对象能够再现目标方法句柄。这意味着如果目标是直接方法句柄但是由不相关的查找对象创建的,则破解可能会失败。如果方法句柄是 来电敏感 并且是由不同类的查找对象创建的,就会发生这种情况。- 参数:
target
- 破解符号引用组件的直接方法句柄- 返回:
- 一个符号引用,可用于从此查找对象重建此方法句柄
- 抛出:
SecurityException
- 如果存在安全管理器并且它 拒绝访问IllegalArgumentException
- 如果目标不是直接方法句柄或者访问检查失败NullPointerException
- 如果目标是null
- 自从:
- 1.8
- 参见:
-
hasPrivateAccess
已弃用。如果此查找具有PRIVATE
和MODULE
访问权限,则返回true
。- 返回:
true
如果此查找具有PRIVATE
和MODULE
访问权限。- 自从:
- 9
-
hasFullPrivilegeAccess
public boolean hasFullPrivilegeAccess()如果此查找具有 full privilege access,即PRIVATE
和MODULE
访问权限,则返回true
。Lookup
对象必须具有完全权限访问权限才能访问允许访问 查找类 的所有成员。- 返回:
true
如果此查找具有完全权限访问。- 自从:
- 14
- 参见:
-
PRIVATE
访问,这意味着完全权限访问,但MODULE
访问已独立于PRIVATE
访问。