- 封闭类:
-
MethodHandles
需要创建方法句柄的查找类将调用MethodHandles.lookup
来为自身创建一个工厂。创建Lookup
工厂对象时,确定查找类的身份,并安全地存储在Lookup
对象中。然后,查找类(或其代理)可以使用Lookup
对象上的工厂方法来为允许查找类的所有方法、构造函数和字段创建方法句柄,甚至包括私有方法。
查找工厂方法
Lookup
对象上的工厂方法对应于方法、构造函数和字段的所有主要用例。由工厂方法创建的每个方法句柄在功能上等同于特定的字节码行为。(字节码行为在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
是正在搜索成员的类或接口,文档中称为查找方法的参数。方法类型MT
由返回类型T
和参数类型序列A*
组成。构造函数还具有参数类型序列A*
,并被视为返回类型为C
的新创建对象。方法类型MT
和字段类型FT
都被文档化为名为type
的参数。形式参数this
代表类型为C
的自引用;如果存在,它始终是方法句柄调用的主要参数。(在某些protected
成员的情况下,this
可能在类型上受到查找类的限制;请参见下文。)名称arg
代表所有其他方法句柄参数。在核心反射API的代码示例中,名称thisOrNull
表示如果访问的方法或字段是静态的,则为null引用,否则为this
。名称aMethod
、aField
和aConstructor
表示与类型C
中声明的给定成员对应的反射对象。
findClass
操作的字节码行为是加载常量类,就像通过ldc CONSTANT_Class
一样。该行为表示为常量类,而不是方法句柄。
在给定成员为可变参数性质(即方法或构造函数)的情况下,返回的方法句柄也将是可变参数性质。在所有其他情况下,返回的方法句柄将是固定参数性质。
讨论:查找到的方法句柄与底层类成员和字节码行为之间的等价性可能会以几种方式发生变化:
- 如果从查找类的加载程序无法符号访问
C
,查找仍然可以成功,即使没有等效的Java表达式或字节码常量。 - 同样,如果
T
或MT
从查找类的加载程序无法符号访问,查找仍然可以成功。例如,对于MethodHandle.invokeExact
和MethodHandle.invoke
的查找将始终成功,无论请求的类型如何。 - 如果安装了安全管理器,它可以基于各种原因禁止查找(见下文)。相比之下,
CONSTANT_MethodHandle
常量上的ldc
指令不受安全管理器检查。 - 如果查找的方法具有非常大的参数数量,则由于方法句柄类型具有太多参数,方法句柄创建可能会失败,并引发
IllegalArgumentException
。
访问检查
在Lookup
的工厂方法中应用访问检查,当创建方法句柄时。这与核心反射API的关键区别在于,因为java.lang.reflect.Method.invoke
对每个调用者的每次调用执行访问检查。
所有访问检查都始于一个Lookup
对象,该对象将其记录的查找类与所有请求创建方法句柄进行比较。单个Lookup
对象可用于创建任意数量的经过访问检查的方法句柄,所有这些方法句柄都针对单个查找类进行检查。
Lookup
对象可以与其他受信任的代码共享,例如元对象协议。共享的Lookup
对象委托于在查找类的私有成员上创建方法句柄的能力。即使特权代码使用Lookup
对象,访问检查也限于原始查找类的权限。
查找可能会失败,因为包含类对查找类不可访问,或者因为所需类成员丢失,或者因为所需类成员对查找类不可访问,或者因为查找对象不足以访问成员。在对final
字段上的字段设置函数的情况下,最终性强制执行被视为一种访问控制,并且查找将失败,除非在Lookup.unreflectSetter
的特殊情况下。在任何这些情况下,将从尝试的查找中抛出ReflectiveOperationException
。确切的类将是以下之一:
- NoSuchMethodException — 如果请求方法不存在
- NoSuchFieldException — 如果请求字段不存在
- IllegalAccessException — 如果成员存在但访问检查失败
一般来说,查找方法句柄的方法M
的条件不会比查找类编译、验证和解析对M
的调用的条件更为严格。在JVM会引发NoSuchMethodError
等异常的情况下,方法句柄查找通常会引发相应的已检查异常,例如NoSuchMethodException
。并且调用查找结果的方法句柄的效果是完全等效于执行对M
的编译、验证和解析调用。字段和构造函数的情况也是如此。
讨论: 访问检查仅适用于具名和反射方法、构造函数和字段。其他方法句柄创建方法,如MethodHandle.asType
,不需要任何访问检查,并且独立于任何Lookup
对象使用。
如果所需成员是protected
,则通常的JVM规则适用,包括查找类必须与所需成员位于相同的包中,或者必须继承该成员。 (请参阅Java虚拟机规范,第4.9.2节,5.4.3.5节和6.4节。)此外,如果所需成员是不同包中的非静态字段或方法,则生成的方法句柄只能应用于查找类或其子类的对象。此要求通过将领先的this
参数的类型从C
(必然是查找类的超类)缩小到查找类本身来实施。
JVM对invokespecial
指令施加类似要求,即接收器参数必须与已解析方法和当前类匹配。同样,此要求通过缩小导致方法句柄的领先参数的类型来实施。 (请参阅Java虚拟机规范,第4.10.1.9节。)
JVM将构造函数和静态初始化块表示为具有特殊名称的内部方法("<init>"和"<clinit>")。调用指令的内部语法允许它们引用这些内部方法,就好像它们是普通方法一样,但是JVM字节码验证器会拒绝它们。查找此类内部方法将产生NoSuchMethodException
。
如果嵌套类型之间的关系直接通过NestHost
和NestMembers
属性(请参阅Java虚拟机规范,第4.7.28节和4.7.29节)表达,则相关的Lookup
对象提供对查找类及其所有嵌套成员的直接访问(请参阅Class.getNestHost
)。否则,嵌套类之间的访问是通过Java编译器创建包装方法来访问同一嵌套中另一个类的私有方法而获得的。例如,嵌套类C.D
可以访问其他相关类(如C
、C.D.E
或C.B
)中的私有成员,但是Java编译器可能需要在这些相关类中生成包装方法。在这种情况下,对C.E
上的Lookup
对象将无法访问这些私有成员。解决此限制的方法是Lookup.in
方法,它可以将对C.E
的查找转换为对这些其他类之一的查找,而无需特殊权限提升。
给定查找对象允许的访问可能会受到其lookupModes
集的限制,仅允许访问通常对查找类可访问的成员的子集。例如,publicLookup
方法生成一个只允许访问导出包的公共类中的公共成员的查找对象。调用者敏感方法lookup
生成一个相对于其调用类具有完整功能的查找对象,以模拟所有支持的字节码行为。此外,Lookup.in
方法可能生成一个具有比原始查找对象更少访问模式的查找对象。
私有和模块访问的讨论: 如果查找的查找模式包括访问private
成员(包括嵌套成员的私有成员),我们称之为查找具有私有访问。正如在其他相关方法中记录的,只有具有私有访问权限的查找具有以下功能:
- 访问查找类及其嵌套成员的私有字段、方法和构造函数
- 创建方法句柄,
模拟invokespecial
指令 - 避免对查找类可访问的类进行包访问检查
- 创建具有对同一包成员内其他类的私有访问权限的
委托查找对象
类似地,具有模块访问权限的查找确保原始查找创建者是与查找类相同模块的成员。
私有和模块访问是独立确定的模式;查找可以具有私有访问、模块访问或两者或两者都没有。同时具有这两种访问模式的查找被称为具有完全特权访问。
具有原始访问的查找确保此查找是由原始查找类和VM调用的引导方法创建的。具有原始访问权限的查找还具有私有和模块访问权限,具有以下附加功能:
这些权限的每一个都是由于具有私有访问权限的查找对象可以安全地追溯到一个原始类,其字节码行为和Java语言访问权限可以被方法句柄可靠确定和模拟。
跨模块查找
当一个模块中的查找类M1
访问另一个模块中的类M2
时,除了访问模式位之外,还会执行额外的访问检查。具有M1
中查找类和PUBLIC
模式的Lookup
可以在M1
可读取M2
且类型在M2
的导出包至少对M1
可见时访问M2
中的公共类型。
Lookup
可以通过Lookup.in
和MethodHandles.privateLookupIn
方法也可以传送到目标类。跨模块传送将始终记录原始查找类作为上一个查找类,并且会丢弃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)
可以用于将来自类C
的lookup
传送到类T
并生成一个新的Lookup
,如果查找类被允许对T
进行深度反射。 lookup
必须具有调用privateLookupIn
的MODULE
和PRIVATE
访问权限。在模块M1
中对C
进行的查找允许对M1
中的所有类进行深度反射。如果T
在M1
中,privateLookupIn
会在T
上生成一个具有完整功能的新Lookup
。在模块M1
中对C
进行的查找还允许对另一个模块M2
中的T
进行深度反射,如果M1
读取M2
并且M2
通过opens
至少将包含T
的包对M1
开放。 T
成为新的查找类,C
成为新的先前查找类,并且从生成的Lookup
中删除了MODULE
访问权限。生成的Lookup
可用于执行成员查找或通过调用Lookup::in
传送到另一个查找类。但是,它不能通过调用privateLookupIn
来获取另一个私有Lookup
,因为它没有MODULE
访问权限。
privateLookupIn
返回的Lookup
对象允许在T
的运行时包中定义类。在向另一个模块开放包时应格外小心,因为这样定义的类具有与M2
中其他成员相同的完全特权访问权限。
跨模块访问检查
具有PUBLIC
或 UNCONDITIONAL
模式的 Lookup
允许跨模块访问。访问检查是相对于查找类和先前查找类(如果存在)进行的。
具有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
的合格导出包 - 如果
M0
和M1
都读取M2
,则来自第三个模块M2
到M0
和M1
的合格导出包
访问模式
下表显示了由以下任何工厂或转换方法生成的Lookup
的访问模式:
MethodHandles::lookup
MethodHandles::publicLookup
MethodHandles::privateLookupIn
Lookup::in
Lookup::dropLookupMode
查找对象 | 原始 | 受保护的 | 私有的 | 包级别 | 模块 | 公共的 |
---|---|---|---|---|---|---|
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
位设置,1R
和2R
表示PUBLIC
位设置,U
表示UNCONDITIONAL
位设置,IAE
表示抛出IllegalAccessException
。- 公共访问有三种类型:
- 无条件的(
U
):查找假定可读性。查找类为null
。 - 单模块读取(
1R
):模块访问检查是相对于查找类执行的。查找类为null
。 - 双模块读取(
2R
):模块访问检查是相对于查找类和前一个查找类执行的。查找类具有非空的前一个查找类,该类位于与当前查找类不同的模块中。
- 无条件的(
- 任何尝试到达第
getfield
。有一个安全管理器API允许应用程序检查这种跨加载器引用。这些检查适用于MethodHandles.Lookup
API和核心反射API(如Class
中所找到的)。如果存在安全管理器,则成员和类查找将受到额外检查的限制。安全管理器会进行一到三次调用。其中任何一次调用都可以通过抛出一个
SecurityException
来拒绝访问。将smgr
定义为安全管理器,将lookc
定义为当前查找对象的查找类,将refc
定义为正在寻找成员的包含类,将defc
定义为实际定义成员的类(如果正在访问类或其他类型,则refc
和defc
的值为类本身)。如果当前查找对象没有完全特权访问,则将值lookc
定义为不存在。根据以下规则进行调用:- 步骤1: 如果
lookc
不存在,或者其类加载器与refc
的类加载器不同或不是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方法具有称为调用者敏感性的特殊属性。一个调用者敏感方法的行为可能会根据其直接调用者的身份而有所不同。如果请求调用者敏感方法的方法句柄,则将应用一般的字节码行为规则,但会以特殊方式考虑查找类。生成的方法句柄的行为就好像它是从包含在查找类中的指令中调用的一样,因此调用者敏感方法会检测查找类。(相比之下,方法句柄的调用者被忽略。)因此,在调用者敏感方法的情况下,不同的查找类可能导致行为不同的方法句柄。
在查找对象为
publicLookup()
或其他没有原始访问的查找对象的情况下,查找类将被忽略。在这种情况下,无法创建调用者敏感方法句柄,访问被禁止,并且查找失败并出现IllegalAccessException
。讨论:例如,调用者敏感方法
Class.forName(x)
可能返回不同的类或根据调用它的类的类加载器抛出不同的异常。对于Class.forName
的公共查找将失败,因为无法合理确定其字节码行为。如果应用程序为广泛共享缓存方法句柄,则应使用
publicLookup()
来创建它们。如果有一个Class.forName
的查找,它将失败,并且应用程序必须在这种情况下采取适当的措施。可能在稍后的查找中,也许在调用引导方法时,可以将调用者的具体身份纳入其中,从而使方法可访问。函数
MethodHandles.lookup
是调用者敏感的,以便为查找提供安全基础。JSR 292 API中几乎所有其他方法都依赖于查找对象来检查访问请求。 - 步骤1: 如果
-
Nested Class Summary
Modifier and TypeClassDescriptionstatic enum
一组类选项,指定由Lookup::defineHiddenClass
方法创建的隐藏类是否动态添加为查找类的新成员,和/或隐藏类是否与标记为其定义加载器的类加载器具有强关联。 -
Field Summary
Modifier and TypeFieldDescriptionstatic 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
的结果。 -
Method Summary
Modifier and TypeMethodDescription<T> Class
<T> accessClass
(Class<T> targetClass) 确定一个类是否可以从由此Lookup
对象定义的查找上下文中访问。bind
(Object receiver, String name, MethodType type) 为非静态方法生成早期绑定的方法句柄。Class
<?> defineClass
(byte[] bytes) defineHiddenClass
(byte[] bytes, boolean initialize, MethodHandles.Lookup.ClassOption... options) 从bytes
创建一个隐藏类或接口,返回新创建的类或接口上的Lookup
。defineHiddenClassWithClassData
(byte[] bytes, Object classData, boolean initialize, MethodHandles.Lookup.ClassOption... options) dropLookupMode
(int modeToDrop) 在此查找对象找到成员的相同查找类上创建一个查找,但具有已失去给定查找模式的查找模式。<T> Class
<T> ensureInitialized
(Class<T> targetClass) 确保targetClass
已被初始化。Class
<?> 通过此Lookup
对象定义的查找上下文按名称查找一个类,就像通过ldc
指令解析一样。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
的类中声明的静态字段name
的访问。findVarHandle
(Class<?> recv, String name, Class<?> type) 生成一个VarHandle,提供对类型为recv
的类中声明的非静态字段name
的访问。findVirtual
(Class<?> refc, String name, MethodType type) 为虚拟方法生成一个方法句柄。boolean
如果此查找具有完全特权访问,则返回true
。boolean
已弃用。在指定的新查找类上创建一个查找。Class
<?> 告诉执行查找的类是哪个。int
告诉此查找对象可以生成哪些成员的访问保护类。Class
<?> 报告此查找对象先前从中传送的另一个模块中的查找类,或null
。revealDirect
(MethodHandle target) 破解由此查找对象或类似对象创建的直接方法句柄。toString()
显示要进行查找的类的名称,后跟“/”和previousLookupClass()
的名称(如果存在)。如果查找类具有权限,则创建一个到m的直接方法句柄。生成一个反射构造函数的方法句柄。生成一个方法句柄,提供对反射字段的读取访问。生成一个方法句柄,提供对反射字段的写入访问。unreflectSpecial
(Method m, Class<?> specialCaller) 生成一个反射方法的方法句柄。生成一个VarHandle,提供对类型为R
的类中声明的类型为T
的反射字段f
的访问。
-
Field Details
-
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
假定可读性。当类型位于无条件导出
的包中时,此查找模式可以访问所有模块的公共类型的公共成员。如果设置了此查找模式,则先前查找类始终为
null
。- 自:
- 9
- 参见:
-
ORIGINAL
public static final int ORIGINAL一个表示original
访问的单比特掩码,可能会影响lookupModes
的结果。该值为0x40
,与任何特定的修饰符位没有明显对应关系。如果设置了此查找模式,则
Lookup
对象必须通过调用MethodHandles.lookup()
方法或由VM调用的引导方法由原始查找类创建。具有此查找模式的Lookup
对象具有完全特权访问。- 自:
- 16
- 另请参见:
-
-
Method Details
-
lookupClass
指示执行查找的类。对于可见性和访问权限,将对此类执行检查。如果此查找对象具有先前的查找类,则将对查找类和先前的查找类执行访问检查。
该类暗示了最大级别的访问权限,但权限可能受到位掩码
lookupModes
的额外限制,该位控制是否可以访问非公共成员。- 返回:
- 查找类,代表此查找对象查找成员
- 另请参见:
-
previousLookupClass
报告此查找对象以前从中传送的另一个模块中的查找类,或null
。由工厂方法生成的
Lookup
对象,例如lookup()
和publicLookup()
方法,具有null
先前的查找类。当此查找从一个模块中的旧查找类传送到另一个模块中的新查找类时,Lookup
对象具有非空先前的查找类。- 返回:
-
此查找对象以前从中传送的另一个模块中的查找类,或
null
- 自:
- 14
- 另请参见:
-
lookupModes
public int lookupModes()指示此查找对象可以生成哪些成员的访问保护类。结果是位掩码的位PUBLIC (0x01)、PRIVATE (0x02)、PROTECTED (0x04)、PACKAGE (0x08)、MODULE (0x10)、UNCONDITIONAL (0x20)和ORIGINAL (0x40)。在
caller's class
上创建的新创建的查找对象具有除UNCONDITIONAL
之外的所有可能位设置。从先前查找对象创建的新查找类上的查找对象可能会将某些模式位设置为零。模式位也可以直接清除。一旦清除,模式位就无法从降级的查找对象中恢复。其目的是限制通过新查找对象的访问,使其只能访问原始查找对象和新查找类可以访问的名称。- 返回:
- 限制此查找对象执行的访问种类的查找模式
- 另请参见:
-
in
在指定的新查找类上创建一个查找。生成的对象将报告指定的类作为其自己的lookupClass
。但是,生成的
Lookup
对象保证不会具有比原始对象更多的访问权限。特别是,访问权限可能会丢失如下:- 如果新查找类与旧查找类不同,即丢失
ORIGINAL
访问。 - 如果新查找类与旧查找类不同模块,即丢失
MODULE
访问。 - 如果新查找类与旧查找类不同包,受保护和默认(包)成员将无法访问,即丢失
PROTECTED
和PACKAGE
访问。 - 如果新查找类不在与旧查找类相同的包成员中,私有成员将无法访问,并且受保护成员将无法通过继承访问,即丢失
PRIVATE
访问(由于包共享,受保护成员可能仍然可访问)。 - 如果新查找类对此查找不可访问,则没有成员,甚至没有公共成员可访问,即所有访问模式都丢失。
- 如果新查找类、旧查找类和先前查找类都在不同模块中,即传送到第三个模块,所有访问模式都丢失。
新的先前查找类选择如下:
- 如果新查找对象具有
UNCONDITIONAL
位,则新的先前查找类为null
。 - 如果新查找类与旧查找类在同一模块中,则新的先前查找类为旧的先前查找类。
- 如果新查找类与旧查找类不同模块,则新的先前查找类为旧查找类。
由于此操作可能导致查找类的加载能力发生变化,因此生成的查找的加载类的能力(在
findClass(java.lang.String)
调用期间使用)由查找类的加载器确定。- 参数:
-
requestedLookupClass
- 新查找对象的期望查找类 - 返回:
- 报告所需查找类的查找对象,如果没有更改则为相同对象
- 抛出:
-
IllegalArgumentException
- 如果requestedLookupClass
是原始类型或void或数组类 -
NullPointerException
- 如果参数为null - 另请参见:
- 如果新查找类与旧查找类不同,即丢失
-
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
访问,而不授予包访问。具有previous lookup class
(和PUBLIC
但不具有MODULE
模式)的查找可以安全地委托访问对于查找类的模块和先前查找类的模块都可访问的公共类。 - 参数:
-
modeToDrop
- 要删除的查找模式 - 返回:
- 缺少指定模式的查找对象,如果没有更改则为相同对象
- 抛出:
-
IllegalArgumentException
- 如果modeToDrop
不是PUBLIC
、MODULE
、PACKAGE
、PROTECTED
、PRIVATE
、ORIGINAL
或UNCONDITIONAL
之一 - 自:
- 9
- 另请参见:
-
defineClass
创建并链接一个与此查找的查找类相同的类或接口,使用相同的类加载器和相同的运行时包和保护域,就像调用ClassLoader::defineClass
一样。此查找的查找模式必须包括默认(包)访问权限,因为默认成员将对该类可访问。
PACKAGE
查找模式用于验证查找对象是否由运行时包中的调用者创建(或者是否由适当特权代码最初创建的查找对象派生自运行时包中的目标类)。bytes
参数是有效类文件的类字节(由Java虚拟机规范定义),其类名与查找类相同包中。此方法不运行类初始化程序。类初始化程序可能会在稍后的时间运行,详细信息请参阅Java语言规范第12.4节。
如果存在安全管理器并且此查找没有
full privilege access
,则首先调用其checkPermission
方法来检查RuntimePermission("defineClass")
。- 参数:
-
bytes
- 类字节 - 返回:
-
类的
Class
对象 - 抛出:
-
IllegalAccessException
- 如果此查找没有PACKAGE
访问权限 -
ClassFormatError
- 如果bytes
不是ClassFile
结构 -
IllegalArgumentException
- 如果bytes
表示与查找类不同包中的类或接口(access_flags
项的值中设置了ACC_MODULE
标志) -
VerifyError
- 如果新创建的类无法验证 -
LinkageError
- 如果由于任何其他原因而无法链接新创建的类 -
SecurityException
- 如果存在安全管理器并且它拒绝访问 -
NullPointerException
- 如果bytes
为null
- 自:
- 9
- 参见:
-
defineHiddenClass
public MethodHandles.Lookup defineHiddenClass(byte[] bytes, boolean initialize, MethodHandles.Lookup.ClassOption... options) throws IllegalAccessException 创建一个隐藏的类或接口,从bytes
中返回一个新创建的类或接口的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
的查找类相同的运行时包、模块和保护域。- 设
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
,则Java虚拟机会初始化C
。新创建的类或接口
C
作为此方法返回的Lookup
对象的查找类。C
是隐藏的,意味着没有其他类或接口可以通过常量池条目引用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可能会耗尽内存,就像通过类加载器创建正常类一样。嵌套中的类和接口允许彼此访问其私有成员。嵌套关系由
NestHost
属性(JVMS 4.7.28)和NestMembers
属性(JVMS 4.7.29)在class
文件中确定。默认情况下,隐藏的类属于仅包含自身的嵌套。可以通过在options
中传递NESTMATE
选项来创建一个隐藏的类或接口C
作为嵌套的成员。C
所属的嵌套不基于从中派生C
的ClassFile
结构中的任何NestHost
属性。相反,以下规则确定C
的嵌套主机:- 如果此
Lookup
的查找类的嵌套主机先前已确定,则让H
为查找类的嵌套主机。否则,使用JVMS 5.4.4中的算法确定查找类的嵌套主机,得到H
。 C
的嵌套主机被确定为H
,查找类的嵌套主机。
隐藏的类或接口可能是可序列化的,但这需要一种自定义序列化机制,以确保实例被正确序列化和反序列化。默认的序列化机制仅支持通过其类名发现的类和接口。
- 参数:
-
bytes
- 组成类数据的字节,格式符合Java虚拟机规范中定义的有效class
文件格式。 -
initialize
- 如果为true
,则将初始化该类。 -
options
- 类选项 - 返回:
-
针对隐藏类的
Lookup
对象,具有原始和完全特权访问权限 - 抛出:
-
IllegalAccessException
- 如果此Lookup
没有完全特权访问权限 -
SecurityException
- 如果存在安全管理器并且拒绝访问 -
ClassFormatError
- 如果bytes
不是ClassFile
结构 -
UnsupportedClassVersionError
- 如果bytes
不是受支持的主要或次要版本 -
IllegalArgumentException
- 如果bytes
表示与查找类不同包中的类,或者bytes
不是类或接口(在access_flags
项的值中设置了ACC_MODULE
标志) -
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
文件表示中派生Class
5.4 链接
5.5 初始化
- 自版本:
- 15
- 另请参见:
- 此
-
defineHiddenClassWithClassData
public MethodHandles.Lookup defineHiddenClassWithClassData(byte[] bytes, Object classData, boolean initialize, MethodHandles.Lookup.ClassOption... options) throws IllegalAccessException 从bytes
创建一个隐藏类或接口,并使用关联的类数据,返回新创建的类或接口上的Lookup
。此方法等同于调用
defineHiddenClass(bytes, initialize, options)
,就好像隐藏类被注入了一个私有的静态最终的未命名字段,在类初始化器的第一条指令处使用给定的classData
进行初始化。新创建的类由Java虚拟机链接。可以使用
MethodHandles::classData
和MethodHandles::classDataAt
方法检索classData
。- API注释:
-
框架可以使用一个或多个对象创建带有类数据的隐藏类,并通过引导方法将类数据加载为动态计算的常量。
类数据
仅对由新定义的隐藏类创建的查找对象可访问,但对同一巢中的其他成员不可访问(不像对巢成员可见的私有静态字段)。在传递数组或其他可变结构时,应注意可变性,例如在运行时更改类数据中存储的任何值可能导致不可预测的行为。如果类数据是一个List
,最好通过List::of
等方法使其不可修改。 - 参数:
-
bytes
- 类字节 -
classData
- 预初始化的类数据 -
initialize
- 如果为true
,则将初始化该类。 -
options
- 类选项 - 返回:
-
针对隐藏类的
Lookup
对象,具有原始和完全特权访问权限 - 抛出:
-
IllegalAccessException
- 如果此Lookup
没有完全特权访问权限 -
SecurityException
- 如果存在安全管理器并且拒绝访问 -
ClassFormatError
- 如果bytes
不是ClassFile
结构 -
UnsupportedClassVersionError
- 如果bytes
不是受支持的主要或次要版本 -
IllegalArgumentException
- 如果bytes
表示与查找类不同包中的类,或者bytes
不是类或接口(在access_flags
项的值中设置了ACC_MODULE
标志) -
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
文件表示中派生Class
5.4 链接
5.5 初始化
- 自版本:
- 16
- 另请参见:
-
toString
显示要进行查找的类的名称,后跟“/”和上一个查找类的名称(如果存在)。 (名称是由Class.getName
报告的名称。)如果对此查找允许访问的权限有限制,则通过在类名后添加后缀来指示,后缀由斜杠和关键字组成。关键字代表允许的最强访问权限,并按照以下方式选择:- 如果不允许任何访问权限,则后缀为“/noaccess”。
- 如果仅允许无条件访问,则后缀为“/publicLookup”。
- 如果仅允许对导出包中的类型进行公共访问,则后缀为“/public”。
- 如果仅允许公共和模块访问,则后缀为“/module”。
- 如果允许公共和包访问,则后缀为“/package”。
- 如果允许公共、包和私有访问,则后缀为“/private”。
MethodHandles.lookup
获取的对象。由Lookup.in
创建的对象始终具有受限制的访问权限,并将显示后缀。(保护访问权限应该比私有访问权限更强可能看起来有些奇怪。从包访问独立查看时,保护访问权限是首先丢失的,因为它要求调用者和被调用者之间存在直接的子类关系。)
-
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
,或者方法的可变性修饰符位已设置且asVarargsCollector
失败 -
SecurityException
- 如果存在安全管理器并且它拒绝访问 -
NullPointerException
- 如果任何参数为null
-
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
使用相同type
参数生成的方法句柄。如果类为
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")); // 接口方法: MethodHandle MH_subSequence = publicLookup().findVirtual(CharSequence.class, "subSequence", methodType(CharSequence.class, int.class, int.class)); assertEquals("def", MH_subSequence.invoke("abcdefghi", 3, 6).toString()); // 构造函数“内部方法”必须以不同方式访问: 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
,或者方法的可变性修饰符位已设置且asVarargsCollector
失败 -
SecurityException
- 如果存在安全管理器并且它拒绝访问 -
NullPointerException
- 如果任何参数为null
-
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); // 一个可变参数构造函数: 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
- 如果访问检查失败或者方法的可变参数修饰符位被设置且asVarargsCollector
失败 -
SecurityException
- 如果存在安全管理器并且它拒绝访问 -
NullPointerException
- 如果任何参数为null
-
findClass
从此Lookup
对象定义的查找上下文中按名称查找类,就像通过ldc
指令解析一样。如JVMS中5.4.3.1中指定的那样,此类解析尝试定位和加载类,然后确定该类是否对此查找对象可访问。对于类或接口,名称是二进制名称。对于
n
维数组类,名称以n
个'['
开始,后跟元素类型,如在表中指定的那样在Class.getName()
中编码。- 参数:
-
targetName
- 类的二进制名称或表示数组类的字符串 - 返回:
- 请求的类
- 抛出:
-
SecurityException
- 如果存在安全管理器并且它拒绝访问 -
LinkageError
- 如果链接失败 -
ClassNotFoundException
- 如果类无法被查找类的加载器加载 -
IllegalAccessException
- 如果类不可访问,使用允许的访问模式 -
NullPointerException
- 如果targetName
为null - 参见Java虚拟机规范:
-
5.4.3.1 类和接口解析
- 自Java版本:
- 9
-
ensureInitialized
确保targetClass
已被初始化。要初始化的类必须对此Lookup
对象可访问。此方法导致targetClass
在尚未初始化时被初始化,如JVMS中5.5中指定。当
targetClass
完全初始化时,或者当targetClass
正在被当前线程初始化时,此方法返回。- 类型参数:
-
T
- 要初始化的类的类型 - 参数:
-
targetClass
- 要初始化的类 - 返回:
-
已被初始化的
targetClass
,或者正在被当前线程初始化的targetClass
- 抛出:
-
IllegalArgumentException
- 如果targetClass
是原始类型或void
或数组类 -
IllegalAccessException
- 如果targetClass
对此查找不可访问 -
ExceptionInInitializerError
- 如果此方法引发的类初始化失败 -
SecurityException
- 如果存在安全管理器并且它拒绝访问 - 参见Java虚拟机规范:
-
5.5 初始化
- 自Java版本:
- 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
是公共的,并且在至少向M1
导出的M2
中的包中。
否则,如果此查找有先前的查找类,
M1
和M2
如前所述,M0
是包含先前查找类的模块,则只有以下情况之一为真时targetClass
才可访问:targetClass
在M0
中,M1
读取M0
,并且类型在导出至至少M1
的包中。targetClass
在M1
中,M0
读取M1
,并且类型在导出至至少M0
的包中。targetClass
在第三个模块M2
中,M0
和M1
都读取M2
,并且类型在导出至至少M0
和M2
的包中。
否则,
targetClass
不可访问。- 类型参数:
-
T
- 要进行访问检查的类的类型 - 参数:
-
targetClass
- 要进行访问检查的类 - 返回:
-
已进行访问检查的
targetClass
- 抛出:
-
IllegalAccessException
- 如果类从查找类和先前查找类(如果存在)不可访问,使用允许的访问模式 -
SecurityException
- 如果存在安全管理器并且它拒绝访问 -
NullPointerException
- 如果targetClass
为null
- 自Java版本:
- 9
- 参见:
- 如果此查找具有
-
findSpecial
public MethodHandle findSpecial(Class<?> refc, String name, MethodType type, Class<?> specialCaller) throws NoSuchMethodException, IllegalAccessException 生成一个虚拟方法的早期绑定方法句柄。它将绕过接收者上重写方法的检查,就像从显式指定的specialCaller
内的invokespecial
指令调用一样。方法句柄的类型将是方法的类型,前面会添加一个适当限制的接收者类型。(接收者类型将是specialCaller
或其子类型。)方法及其所有参数类型必须对查找对象可访问。在方法解析之前,如果显式指定的调用者类与查找类不相同,或者如果此查找对象没有私有访问权限,则访问将失败。
如果方法的可变参数修饰符位(
0x0080
)已设置,则返回的方法句柄将具有可变参数性质。(注意:JVM内部方法命名为"<init>"的方法对于此API不可见,即使
invokespecial
指令在特殊情况下可以引用它们。请使用findConstructor
以安全方式访问实例初始化方法。)示例:
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(); } } ... // 通过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 // 通过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
,或者如果方法的可变参数修饰符位已设置且asVarargsCollector
失败 -
SecurityException
- 如果存在安全管理器并且它拒绝访问 -
NullPointerException
- 如果任何参数为null
-
findGetter
public MethodHandle findGetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException 生成一个方法句柄,允许读取非静态字段。方法句柄的类型将具有字段值类型的返回类型。方法句柄的单个参数将是包含字段的实例。立即代表查找类执行访问检查。- 参数:
-
refc
- 访问方法的类或接口 -
name
- 字段的名称 -
type
- 字段的类型 - 返回:
- 一个可以从字段加载值的方法句柄
- 抛出:
-
NoSuchFieldException
- 如果字段不存在 -
IllegalAccessException
- 如果访问检查失败,或者字段是static
-
SecurityException
- 如果存在安全管理器并且它拒绝访问 -
NullPointerException
- 如果任何参数为null - 参见:
-
findSetter
public MethodHandle findSetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException 生成一个方法句柄,允许写入非静态字段。方法句柄的类型将具有void返回类型。方法句柄将接受两个参数,包含字段的实例和要存储的值。第二个参数将是字段的值类型。立即代表查找类执行访问检查。- 参数:
-
refc
- 访问方法的类或接口 -
name
- 字段的名称 -
type
- 字段的类型 - 返回:
- 一个可以将值存储到字段中的方法句柄
- 抛出:
-
NoSuchFieldException
- 如果字段不存在 -
IllegalAccessException
- 如果访问检查失败,或者字段是static
或final
-
SecurityException
- 如果存在安全管理器并且它拒绝访问 -
NullPointerException
- 如果任何参数为null - 参见:
-
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
时。在执行带有这些值的比较和设置或比较和交换操作时,应当注意,操作可能会意外失败。在Java中有许多可能被视为NaN
的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
- 如果任何参数为null - 自Java版本:
- 9
- 如果字段声明为
-
findStaticGetter
public MethodHandle findStaticGetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException 生成一个方法句柄,提供对静态字段的读取访问。方法句柄的类型将具有字段值类型的返回类型。方法句柄将不带任何参数。立即代表查找类执行访问检查。如果调用返回的方法句柄,则将初始化字段的类,如果尚未初始化。
- 参数:
-
refc
- 访问该方法的类或接口 -
name
- 字段的名称 -
type
- 字段的类型 - 返回:
- 一个方法句柄,可以从字段加载值
- 抛出:
-
NoSuchFieldException
- 如果字段不存在 -
IllegalAccessException
- 如果访问检查失败,或者字段不是static
-
SecurityException
- 如果存在安全管理器并且它拒绝访问 -
NullPointerException
- 如果任何参数为null
-
findStaticSetter
public MethodHandle findStaticSetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException 生成一个方法句柄,提供对静态字段的写入访问。方法句柄的类型将具有void返回类型。方法句柄将接受一个参数,字段值类型的值将被存储。立即代表查找类执行访问检查。如果调用返回的方法句柄,则将初始化字段的类,如果尚未初始化。
- 参数:
-
refc
- 访问该方法的类或接口 -
name
- 字段的名称 -
type
- 字段的类型 - 返回:
- 一个方法句柄,可以将值存储到字段中
- 抛出:
-
NoSuchFieldException
- 如果字段不存在 -
IllegalAccessException
- 如果访问检查失败,或者字段不是static
或者是final
-
SecurityException
- 如果存在安全管理器并且它拒绝访问 -
NullPointerException
- 如果任何参数为null
-
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
时。在执行带有这些值的比较和设置或比较和交换操作时,应当注意,操作可能会意外失败。在Java中有许多可能被视为NaN
的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
- 如果任何参数为null - 自Java版本:
- 9
- 如果字段声明为
-
bind
public MethodHandle bind(Object receiver, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException 生成一个非静态方法的早期绑定方法句柄。接收者必须在其中给定名称和类型的方法对查找类可访问的超类型defc
中。方法及其所有参数类型必须对查找对象可访问。方法句柄的类型将是方法的类型,不会插入额外的接收者参数。给定的接收者将绑定到方法句柄中,因此对方法句柄的每次调用都将在给定的接收者上调用请求的方法。如果方法的可变性修饰符位(
0x0080
)被设置,并且尾随数组参数不是唯一参数,则返回的方法句柄将具有可变数量。(如果尾随数组参数是唯一参数,则给定的接收者值将绑定到它。)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
- 如果访问检查失败或者方法的可变性修饰符位被设置且asVarargsCollector
失败 -
SecurityException
- 如果存在安全管理器并且它拒绝访问 -
NullPointerException
- 如果任何参数为null - 参见:
-
unreflect
如果查找类有权限,为m生成一个直接方法句柄。如果m是非静态的,则接收者参数将被视为初始参数。如果m是虚拟的,则在每次调用时都会尊重覆盖。与核心反射API不同,异常不会被包装。方法句柄的类型将是方法的类型,其中接收者类型被前置(但仅当它是非静态时)。如果方法的accessible
标志未设置,则将立即代表查找类执行访问检查。如果m不是公共的,请勿与不受信任的方共享生成的句柄。如果方法的可变性修饰符位(
0x0080
)被设置,则返回的方法句柄将具有可变数量。如果m是静态的,并且如果调用返回的方法句柄,则如果尚未初始化方法的类,则将初始化它。
- 参数:
-
m
- 反射方法 - 返回:
- 可调用反射方法的方法句柄
- 抛出:
-
IllegalAccessException
- 如果访问检查失败或者方法的可变性修饰符位被设置且asVarargsCollector
失败 -
NullPointerException
- 如果参数为null
-
unreflectSpecial
public MethodHandle unreflectSpecial(Method m, Class<?> specialCaller) throws IllegalAccessException 为反射方法生成一个方法句柄。它将绕过接收者上的覆盖方法的检查,就像从显式指定的specialCaller
内的invokespecial
指令调用一样。方法句柄的类型将是方法的类型,前置一个适当受限制的接收者类型。(接收者类型将是specialCaller
或子类型。)如果方法的accessible
标志未设置,则将立即代表查找类执行访问检查,就像invokespecial
指令正在链接一样。在方法解析之前,如果显式指定的调用者类与查找类不同,或者此查找对象没有私有访问权限,则访问将失败。
如果方法的可变性修饰符位(
0x0080
)被设置,则返回的方法句柄将具有可变数量。- 参数:
-
m
- 反射方法 -
specialCaller
- 名义上调用方法的类 - 返回:
- 可调用反射方法的方法句柄
- 抛出:
-
IllegalAccessException
- 如果访问检查失败,或者方法是static
,或者方法的可变性修饰符位被设置且asVarargsCollector
失败 -
NullPointerException
- 如果任何参数为null
-
unreflectConstructor
为反射构造函数生成一个方法句柄。方法句柄的类型将是构造函数的类型,返回类型更改为声明类。方法句柄将执行newInstance
操作,在传递给方法句柄的参数上创建构造函数类的新实例。accessible
标志未设置,则将立即代表查找类执行访问检查。如果构造函数的可变性修饰符位(
0x0080
)被设置,则返回的方法句柄将具有可变数量。如果调用返回的方法句柄,则如果尚未初始化构造函数的类,则将初始化它。
- 参数:
-
c
- 反射构造函数 - 返回:
- 可调用反射构造函数的方法句柄
- 抛出:
-
IllegalAccessException
- 如果访问检查失败或者方法的可变性修饰符位被设置且asVarargsCollector
失败 -
NullPointerException
- 如果参数为null
-
unreflectGetter
生成一个方法句柄,允许读取反射字段。方法句柄的类型将具有字段值类型的返回类型。如果字段是static
,则方法句柄将不带参数。否则,它的单个参数将是包含字段的实例。如果Field
对象的accessible
标志未设置,则将立即代表查找类执行访问检查。- 参数:
-
f
- 反射字段 - 返回:
- 可从反射字段加载值的方法句柄
- 抛出:
-
IllegalAccessException
- 如果访问检查失败 -
NullPointerException
- 如果参数为null
-
unreflectSetter
生成一个方法句柄,允许写入反射字段。方法句柄的类型将具有void返回类型。如果字段是static
,则方法句柄将带有一个参数,字段值类型的值,即要存储的值。否则,两个参数将是包含字段的实例和要存储的值。如果Field
对象的accessible
标志未设置,则将立即代表查找类执行访问检查。final
,则不允许写入访问,并且访问检查将失败,除非在为Field.set
记录的某些狭窄情况下。仅当对应于Field
对象的set
方法的调用可以正常返回时,才会返回方法句柄。特别是,既是static
又是final
的字段可能永远不会被设置。static
,并且如果调用返回的方法句柄,则如果尚未初始化字段的类,则将初始化它。- 参数:
-
f
- 反射字段 - 返回:
- 可将值存储到反射字段中的方法句柄
- 抛出:
-
IllegalAccessException
- 如果访问检查失败,或者字段是final
且在Field
对象上未启用写入访问 -
NullPointerException
- 如果参数为null
-
unreflectVarHandle
生成一个VarHandle,用于访问类型为T
的反射字段f
,该字段声明在类型为R
的类中。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
时。在使用这些值执行比较和设置或比较和交换操作时应谨慎,因为操作可能会意外失败。在Java中有许多可能被视为NaN
的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
- 如果参数为null - 自版本:
- 9
- 如果字段被声明为
-
revealDirect
破解由此查找对象或类似对象创建的直接方法句柄。将执行安全性和访问检查,以确保此查找对象能够重现目标方法句柄。这意味着如果目标是直接方法句柄,但是由不相关的查找对象创建的,则破解可能会失败。如果方法句柄是调用者敏感的,并且是由不同类的查找对象创建的,则可能会发生这种情况。- 参数:
-
target
- 要破解为符号引用组件的直接方法句柄 - 返回:
- 一个符号引用,可用于从此查找对象重建此方法句柄
- 抛出:
-
SecurityException
- 如果存在安全管理器并且它拒绝访问 -
IllegalArgumentException
- 如果目标不是直接方法句柄或访问检查失败 -
NullPointerException
- 如果目标为null
- 自版本:
- 1.8
- 另请参阅:
-
hasPrivateAccess
Deprecated.This method was originally designed to testPRIVATE
access that implies full privilege access butMODULE
access has since become independent ofPRIVATE
access. It is recommended to callhasFullPrivilegeAccess()
instead.如果此查找具有PRIVATE
和MODULE
访问权限,则返回true
。- 返回:
-
如果此查找具有
PRIVATE
和MODULE
访问权限,则返回true
。 - 自版本:
- 9
-
hasFullPrivilegeAccess
public boolean hasFullPrivilegeAccess()- 返回:
-
如果此查找具有完全特权访问,则返回
true
。 - 自版本:
- 14
- 另请参阅:
-
PRIVATE
访问,但MODULE
访问已经独立于PRIVATE
访问。