Module java.base
Package java.lang.invoke

Class MethodHandles.Lookup

java.lang.Object
java.lang.invoke.MethodHandles.Lookup
封闭类:
MethodHandles

public static final class MethodHandles.Lookup extends Object
查找对象是一个用于创建方法句柄的工厂,当创建需要访问检查时。方法句柄在调用时不执行访问检查,而是在创建时执行。因此,必须在创建方法句柄时执行方法句柄访问限制。对于强制执行这些限制的调用者类称为查找类

需要创建方法句柄的查找类将调用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。名称aMethodaFieldaConstructor表示与类型C中声明的给定成员对应的反射对象。

findClass操作的字节码行为是加载常量类,就像通过ldc CONSTANT_Class一样。该行为表示为常量类,而不是方法句柄。

在给定成员为可变参数性质(即方法或构造函数)的情况下,返回的方法句柄也将是可变参数性质。在所有其他情况下,返回的方法句柄将是固定参数性质。

讨论:查找到的方法句柄与底层类成员和字节码行为之间的等价性可能会以几种方式发生变化:

  • 如果从查找类的加载程序无法符号访问C,查找仍然可以成功,即使没有等效的Java表达式或字节码常量。
  • 同样,如果TMT从查找类的加载程序无法符号访问,查找仍然可以成功。例如,对于MethodHandle.invokeExactMethodHandle.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

如果嵌套类型之间的关系直接通过NestHostNestMembers属性(请参阅Java虚拟机规范,第4.7.28节和4.7.29节)表达,则相关的Lookup对象提供对查找类及其所有嵌套成员的直接访问(请参阅Class.getNestHost)。否则,嵌套类之间的访问是通过Java编译器创建包装方法来访问同一嵌套中另一个类的私有方法而获得的。例如,嵌套类C.D可以访问其他相关类(如CC.D.EC.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.inMethodHandles.privateLookupIn方法也可以传送到目标类。跨模块传送将始终记录原始查找类作为上一个查找类,并且会丢弃MODULE访问。如果目标类与查找类C在同一模块中,则目标类将成为新的查找类,上一个查找类不会更改。如果目标类与M1C的模块)中的不同模块中,则C将成为新的上一个查找类,目标类将成为新的查找类。在这种情况下,如果已经有一个M0中的上一个查找类,并且它与M1M2不同,则生成的查找将丢弃所有特权。例如,

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上的查找,而不会提升特权。如果CD在同一模块中,则lookup2将记录D作为新的查找类,并保留与原始lookup相同的上一个查找类,如果不存在则为null

当一个Lookup从一个嵌套中的类传送到另一个嵌套时,PRIVATE访问将被丢弃。当一个Lookup从一个包中的类传送到另一个包中时,PACKAGE访问将被丢弃。当一个Lookup从一个模块中的类传送到另一个模块中时,MODULE访问将被丢弃。跨模块传送会丢弃访问非导出类的能力,无论是新查找类的模块还是旧查找类的模块,生成的Lookup仅保留PUBLIC访问。一个Lookup可以在查找类的模块和上一个类查找的模块之间来回传送。跨模块传送只能减少访问权限,而不能增加。传送到第三个模块会丢弃所有访问权限。

在上述示例中,如果CD在不同的模块中,lookup2将记录D作为其查找类,C作为其上一个查找类,并且lookup2仅具有PUBLIC访问权限。 lookup2可以传送到C模块和D模块中的其他类。如果类E在第三个模块中,则lookup2.in(E.class)将在E上创建一个没有访问权限的Lookup,并将lookup2的查找类D记录为其上一个查找类。

跨模块传送会限制查找类和上一个查找类均可以平等访问的公共类型的访问权限(请参见下文)。

MethodHandles.privateLookupIn(T.class, lookup)可以用于将来自类Clookup传送到类T并生成一个新的Lookup,如果查找类被允许对T进行深度反射lookup必须具有调用privateLookupInMODULEPRIVATE访问权限。在模块M1中对C进行的查找允许对M1中的所有类进行深度反射。如果TM1中,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中其他成员相同的完全特权访问权限。

跨模块访问检查

具有 PUBLICUNCONDITIONAL 模式的 Lookup 允许跨模块访问。访问检查是相对于查找类和先前查找类(如果存在)进行的。

具有UNCONDITIONAL模式的Lookup可以在类型位于无条件导出的包中时访问所有模块中的公共类型。

如果在M1中对LC进行的Lookup没有先前的查找类,则具有PUBLIC模式的查找可以访问对M1可读且该类型至少对M1导出的包中的所有公共类型。

如果在M1中对LC进行的Lookup具有M0上的先前查找类PLC,则具有PUBLIC模式的查找可以访问对M1可访问的所有公共类型与对M0可访问的所有公共类型的交集。 M0读取M1,因此可访问类型集包括:

  • M1中的无条件导出包
  • 如果M1读取M0,则来自M0的无条件导出包
  • 如果M0M1都读取M2,则来自第三个模块M2的无条件导出包
  • M1M0的合格导出包
  • 如果M1读取M0,则来自M0M1的合格导出包
  • 如果M0M1都读取M2,则来自第三个模块M2M0M1的合格导出包

访问模式

下表显示了由以下任何工厂或转换方法生成的 Lookup 的访问模式:
访问模式摘要
查找对象 原始 受保护的 私有的 包级别 模块 公共的
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 中,但 DD2 在模块 M2 中,而 E 在模块 M3 中。 X 代表对查找不可访问的类。 ANY 代表任何示例查找。
  • ORI 表示 ORIGINAL 位设置,PRO 表示 PROTECTED 位设置,PRI 表示 PRIVATE 位设置,PAC 表示 PACKAGE 位设置,MOD 表示 MODULE 位设置,1R2R 表示 PUBLIC 位设置,U 表示 UNCONDITIONAL 位设置,IAE 表示抛出 IllegalAccessException
  • 公共访问有三种类型:
    • 无条件的(U):查找假定可读性。查找类为 null
    • 单模块读取(1R):模块访问检查是相对于查找类执行的。查找类为 null
    • 双模块读取(2R):模块访问检查是相对于查找类和前一个查找类执行的。查找类具有非空的前一个查找类,该类位于与当前查找类不同的模块中。
  • 任何尝试到达第getfield。有一个安全管理器API允许应用程序检查这种跨加载器引用。这些检查适用于MethodHandles.Lookup API和核心反射API(如Class中所找到的)。

    如果存在安全管理器,则成员和类查找将受到额外检查的限制。安全管理器会进行一到三次调用。其中任何一次调用都可以通过抛出一个SecurityException来拒绝访问。将smgr定义为安全管理器,将lookc定义为当前查找对象的查找类,将refc定义为正在寻找成员的包含类,将defc定义为实际定义成员的类(如果正在访问类或其他类型,则refcdefc的值为类本身)。如果当前查找对象没有完全特权访问,则将值lookc定义为不存在。根据以下规则进行调用:

    • 步骤1: 如果lookc不存在,或者其类加载器与refc的类加载器不同或不是refc的祖先,则调用smgr.checkPackageAccess(refcPkg),其中refcPkgrefc的包。
    • 步骤2a: 如果检索到的成员不是公共的,并且lookc不存在,则调用smgr.checkPermissionRuntimePermission("accessDeclaredMembers")
    • 步骤2b: 如果检索到的类具有null类加载器,并且lookc不存在,则调用smgr.checkPermissionRuntimePermission("getClassLoader")
    • 步骤3: 如果检索到的成员不是公共的,并且lookc不存在,并且defcrefc不同,则调用smgr.checkPackageAccess(defcPkg),其中defcPkgdefc的包。
    在通过其他访问检查后执行安全检查。因此,上述规则假定成员或类是公共的,否则是从具有访问成员或类权限的查找类访问的。

    如果存在安全管理器且当前查找对象没有完全特权访问,则defineClassdefineHiddenClassdefineHiddenClassWithClassData调用smgr.checkPermissionRuntimePermission("defineClass")

    调用者敏感方法

    少数Java方法具有称为调用者敏感性的特殊属性。一个调用者敏感方法的行为可能会根据其直接调用者的身份而有所不同。

    如果请求调用者敏感方法的方法句柄,则将应用一般的字节码行为规则,但会以特殊方式考虑查找类。生成的方法句柄的行为就好像它是从包含在查找类中的指令中调用的一样,因此调用者敏感方法会检测查找类。(相比之下,方法句柄的调用者被忽略。)因此,在调用者敏感方法的情况下,不同的查找类可能导致行为不同的方法句柄。

    在查找对象为publicLookup()或其他没有原始访问的查找对象的情况下,查找类将被忽略。在这种情况下,无法创建调用者敏感方法句柄,访问被禁止,并且查找失败并出现IllegalAccessException

    讨论:例如,调用者敏感方法Class.forName(x)可能返回不同的类或根据调用它的类的类加载器抛出不同的异常。对于Class.forName的公共查找将失败,因为无法合理确定其字节码行为。

    如果应用程序为广泛共享缓存方法句柄,则应使用publicLookup()来创建它们。如果有一个Class.forName的查找,它将失败,并且应用程序必须在这种情况下采取适当的措施。可能在稍后的查找中,也许在调用引导方法时,可以将调用者的具体身份纳入其中,从而使方法可访问。

    函数MethodHandles.lookup是调用者敏感的,以便为查找提供安全基础。JSR 292 API中几乎所有其他方法都依赖于查找对象来检查访问请求。

  • Field Details

    • PUBLIC

      public static final int PUBLIC
      代表public访问的单比特掩码,可能会影响lookupModes的结果。该值0x01恰好与public 修饰符位的值相同。

      具有此查找模式的Lookup执行与查找类和(如果存在)先前查找类相关的跨模块访问检查。

      参见:
    • PRIVATE

      public static final int PRIVATE
      代表private访问的单比特掩码,可能会影响lookupModes的结果。该值0x02恰好与private 修饰符位的值相同。
      参见:
    • PROTECTED

      public static final int PROTECTED
      代表protected访问的单比特掩码,可能会影响lookupModes的结果。该值0x04恰好与protected 修饰符位的值相同。
      参见:
    • PACKAGE

      public static final int PACKAGE
      代表package访问(默认访问)的单比特掩码,可能会影响lookupModes的结果。该值为0x08,与任何特定的修饰符位没有实际对应。
      参见:
    • 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

      public Class<?> lookupClass()
      指示执行查找的类。对于可见性和访问权限,将对此类执行检查。

      如果此查找对象具有先前的查找类,则将对查找类和先前的查找类执行访问检查。

      该类暗示了最大级别的访问权限,但权限可能受到位掩码lookupModes的额外限制,该位控制是否可以访问非公共成员。

      返回:
      查找类,代表此查找对象查找成员
      另请参见:
    • previousLookupClass

      public Class<?> 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

      public MethodHandles.Lookup in(Class<?> requestedLookupClass)
      在指定的新查找类上创建一个查找。生成的对象将报告指定的类作为其自己的lookupClass

      但是,生成的Lookup对象保证不会具有比原始对象更多的访问权限。特别是,访问权限可能会丢失如下:

      • 如果新查找类与旧查找类不同,即丢失ORIGINAL访问。
      • 如果新查找类与旧查找类不同模块,即丢失MODULE访问。
      • 如果新查找类与旧查找类不同包,受保护和默认(包)成员将无法访问,即丢失PROTECTEDPACKAGE访问。
      • 如果新查找类不在与旧查找类相同的包成员中,私有成员将无法访问,并且受保护成员将无法通过继承访问,即丢失PRIVATE访问(由于包共享,受保护成员可能仍然可访问)。
      • 如果新查找类对此查找不可访问,则没有成员,甚至没有公共成员可访问,即所有访问模式都丢失。
      • 如果新查找类、旧查找类和先前查找类都在不同模块中,即传送到第三个模块,所有访问模式都丢失。

      新的先前查找类选择如下:

      • 如果新查找对象具有UNCONDITIONAL位,则新的先前查找类为null
      • 如果新查找类与旧查找类在同一模块中,则新的先前查找类为旧的先前查找类。
      • 如果新查找类与旧查找类不同模块,则新的先前查找类为旧查找类。

      由于此操作可能导致查找类的加载能力发生变化,因此生成的查找的加载类的能力(在findClass(java.lang.String)调用期间使用)由查找类的加载器确定。

      参数:
      requestedLookupClass - 新查找对象的期望查找类
      返回:
      报告所需查找类的查找对象,如果没有更改则为相同对象
      抛出:
      IllegalArgumentException - 如果requestedLookupClass是原始类型或void或数组类
      NullPointerException - 如果参数为null
      另请参见:
    • dropLookupMode

      public MethodHandles.Lookup dropLookupMode(int modeToDrop)
      在此查找对象查找成员的相同查找类上创建一个查找,但具有丢失给定查找模式的查找模式。要删除的查找模式是PUBLICMODULEPACKAGEPROTECTEDPRIVATEORIGINALUNCONDITIONAL之一。

      如果此查找是一个公共查找,则此查找具有设置UNCONDITIONAL模式并且没有其他模式设置。在公共查找上删除UNCONDITIONAL时,结果查找将没有访问权限。

      如果此查找不是公共查找,则无论其查找模式如何,都将始终删除PROTECTEDORIGINAL,因此结果查找模式永远不会具有这些访问权限。删除PACKAGE时,结果查找将不具有PACKAGEPRIVATE访问权限。删除MODULE时,结果查找将不具有MODULEPACKAGEPRIVATE访问权限。删除PUBLIC时,结果查找将没有访问权限。

      API注释:
      具有PACKAGE但不具有PRIVATE模式的查找可以在查找类的包中安全地委托非公共访问,而不授予私有访问。具有MODULE但不具有PACKAGE模式的查找可以在查找类的模块中安全地委托PUBLIC访问,而不授予包访问。具有previous lookup class(和PUBLIC但不具有MODULE模式)的查找可以安全地委托访问对于查找类的模块和先前查找类的模块都可访问的公共类。
      参数:
      modeToDrop - 要删除的查找模式
      返回:
      缺少指定模式的查找对象,如果没有更改则为相同对象
      抛出:
      IllegalArgumentException - 如果modeToDrop不是PUBLICMODULEPACKAGEPROTECTEDPRIVATEORIGINALUNCONDITIONAL之一
      自:
      9
      另请参见:
    • defineClass

      public Class<?> defineClass(byte[] bytes) throws IllegalAccessException
      创建并链接一个与此查找的查找类相同的类或接口,使用相同的类加载器和相同的运行时包和保护域,就像调用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 - 如果bytesnull
      自:
      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

      1. Lookup查找模式必须包括完全特权访问。需要此级别的访问权限才能在此Lookup的查找类的模块中创建C
      2. bytes中的推导表示必须是一个支持的主要版本和次要版本的ClassFile结构(JVMS 4.1)。主要版本和次要版本可能与此Lookup的查找类的class文件版本不同。
      3. this_class的值必须是constant_pool表中的有效索引,并且该索引处的条目必须是有效的CONSTANT_Class_info结构。设N为由此结构指定的以内部形式编码的二进制名称。N必须表示与查找类相同包中的一个类或接口。
      4. CN为字符串N + "." + <suffix>,其中<suffix>是一个未限定名称。

        newBytes为由bytes给出的ClassFile结构,其中在constant_pool表中添加了一个条目,指示CNCONSTANT_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句点而获得的二进制名称。对于表示CClass的实例:

      在派生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所属的嵌套不基于从中派生CClassFile结构中的任何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.28 NestHost属性
      4.7.29 NestMembers属性
      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::classDataMethodHandles::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.28 NestHost属性
      4.7.29 NestMembers属性
      5.4.3.1 类和接口解析
      5.4.4 访问控制
      5.3.5 从class文件表示中派生Class
      5.4 链接
      5.5 初始化
      自版本:
      16
      另请参见:
    • toString

      public String toString()
      显示要进行查找的类的名称,后跟“/”和上一个查找类的名称(如果存在)。 (名称是由Class.getName报告的名称。)如果对此查找允许访问的权限有限制,则通过在类名后添加后缀来指示,后缀由斜杠和关键字组成。关键字代表允许的最强访问权限,并按照以下方式选择:
      • 如果不允许任何访问权限,则后缀为“/noaccess”。
      • 如果仅允许无条件访问,则后缀为“/publicLookup”。
      • 如果仅允许对导出包中的类型进行公共访问,则后缀为“/public”。
      • 如果仅允许公共和模块访问,则后缀为“/module”。
      • 如果允许公共和包访问,则后缀为“/package”。
      • 如果允许公共、包和私有访问,则后缀为“/private”。
      如果以上情况均不适用,则表示允许完全特权访问(公共、模块、包、私有和受保护)。在这种情况下,不添加后缀。这仅适用于最初从MethodHandles.lookup获取的对象。由Lookup.in创建的对象始终具有受限制的访问权限,并将显示后缀。

      (保护访问权限应该比私有访问权限更强可能看起来有些奇怪。从包访问独立查看时,保护访问权限是首先丢失的,因为它要求调用者和被调用者之间存在直接的子类关系。)

      覆盖:
      toString 在类 Object
      返回:
      对象的字符串表示形式。
      参见:
    • findStatic

      public MethodHandle findStatic(Class<?> refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException
      生成静态方法的方法句柄。方法句柄的类型将是方法的类型。(由于静态方法不接受接收者,因此不会将额外的接收者参数插入方法句柄类型中,就像findVirtualfindSpecial那样。)方法及其所有参数类型必须对查找对象可访问。

      如果方法的可变性修饰符位(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中的命名方法。(分派操作与invokevirtualinvokeinterface指令执行的操作相同。)

      如果查找类具有完全访问成员的特权,则第一个参数将是类型refc。否则,成员必须为protected,并且第一个参数在类型上将受到限制以适应查找类。

      如果方法的可变性修饰符位(0x0080)已设置,则返回的方法句柄将具有可变参数性

      由于invokevirtual指令和由findVirtual生成的方法句柄之间的一般等价性,如果类为MethodHandle且名称字符串为invokeExactinvoke,则生成的方法句柄等效于由MethodHandles.exactInvokerMethodHandles.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

      public Class<?> findClass(String targetName) throws ClassNotFoundException, IllegalAccessException
      从此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

      public <T> Class<T> ensureInitialized(Class<T> targetClass) throws IllegalAccessException
      确保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

      public <T> Class<T> accessClass(Class<T> targetClass) throws IllegalAccessException
      确定是否可以从此Lookup对象定义的查找上下文访问类。不运行类的静态初始化程序。如果targetClass是数组类,则只有当数组类的元素类型可访问时,targetClass才可访问。否则,根据以下方式确定targetClass是否可访问。

      如果targetClass与查找类在同一模块中,查找类为模块M1中的LC,先前的查找类在模块M0中或者如果不存在则为null,则只有以下情况之一为真时targetClass才可访问:

      • 如果此查找具有PRIVATE访问权限,则targetClassLCLC的同一巢穴中的其他类。
      • 如果此查找具有PACKAGE访问权限,则targetClassLC的同一运行时包中。
      • 如果此查找具有MODULE访问权限,则targetClassM1中的公共类型。
      • 如果此查找具有PUBLIC访问权限,则targetClass是由M1向至少M0导出的包中的公共类型,如果先前的查找类存在;否则,targetClass是由M1无条件导出的包中的公共类型。

      否则,如果此查找具有UNCONDITIONAL访问权限,则当类型在无条件导出的包中时,此查找可以访问所有模块中的公共类型。

      否则,targetClasslookupClass不在同一模块中,如果此查找没有PUBLIC访问权限,则lookupClass不可访问。

      否则,如果此查找没有先前的查找类M1是包含lookupClass的模块,M2是包含targetClass的模块,则只有以下情况之一为真时targetClass才可访问:

      • M1读取M2,并且
      • targetClass是公共的,并且在至少向M1导出的M2中的包中。

      否则,如果此查找有先前的查找类M1M2如前所述,M0是包含先前查找类的模块,则只有以下情况之一为真时targetClass才可访问:

      • targetClassM0中,M1读取M0,并且类型在导出至至少M1的包中。
      • targetClassM1中,M0读取M1,并且类型在导出至至少M0的包中。
      • targetClass在第三个模块M2中,M0M1都读取M2,并且类型在导出至至少M0M2的包中。

      否则,targetClass不可访问。

      类型参数:
      T - 要进行访问检查的类的类型
      参数:
      targetClass - 要进行访问检查的类
      返回:
      已进行访问检查的targetClass
      抛出:
      IllegalAccessException - 如果类从查找类和先前查找类(如果存在)不可访问,使用允许的访问模式
      SecurityException - 如果存在安全管理器并且它拒绝访问
      NullPointerException - 如果targetClassnull
      自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 - 如果访问检查失败,或者字段是staticfinal
      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,则不支持写入、原子更新、数值原子更新和位原子更新访问模式。
      • 如果字段类型不是byteshortcharintlongfloatdouble,则不支持数值原子更新访问模式。
      • 如果字段类型不是booleanbyteshortcharintlong,则不支持位原子更新访问模式。

      如果字段声明为volatile,则返回的VarHandle将覆盖对字段的访问(实际上忽略volatile声明),以符合其指定的访问模式。

      如果字段类型为floatdouble,则数值和原子更新访问模式将使用它们的位表示进行值比较(参见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,则不支持写入、原子更新、数字原子更新和位原子更新访问模式。
      • 如果字段类型不是byteshortcharintlongfloatdouble,则不支持数字原子更新访问模式。
      • 如果字段类型不是booleanbyteshortcharintlong,则不支持位原子更新访问模式。

      如果字段声明为volatile,则返回的VarHandle将覆盖对字段的访问(实际上忽略volatile声明),以符合其指定的访问模式。

      如果字段类型为floatdouble,则数字和原子更新访问模式将使用它们的位表示进行值比较(分别参见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;
      
      其中 defcreceiver.getClass()或该类的超类型,在该类中请求的方法对查找类可访问。(与 bind不同, bindTo不保留可变数量。此外, bindTo可能会在 bind会抛出 IllegalAccessException的情况下抛出 ClassCastException,例如成员是 protected且接收者受 findVirtual限制为查找类。)
      参数:
      receiver - 访问方法的对象
      name - 方法的名称
      type - 方法的类型,省略了接收者参数
      返回:
      所需的方法句柄
      抛出:
      NoSuchMethodException - 如果方法不存在
      IllegalAccessException - 如果访问检查失败或者方法的可变性修饰符位被设置且asVarargsCollector失败
      SecurityException - 如果存在安全管理器并且它拒绝访问
      NullPointerException - 如果任何参数为null
      参见:
    • unreflect

      public MethodHandle unreflect(Method m) throws IllegalAccessException
      如果查找类有权限,为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

      public MethodHandle unreflectConstructor(Constructor<?> c) throws IllegalAccessException
      为反射构造函数生成一个方法句柄。方法句柄的类型将是构造函数的类型,返回类型更改为声明类。方法句柄将执行newInstance操作,在传递给方法句柄的参数上创建构造函数类的新实例。 accessible标志未设置,则将立即代表查找类执行访问检查。

      如果构造函数的可变性修饰符位(0x0080)被设置,则返回的方法句柄将具有可变数量

      如果调用返回的方法句柄,则如果尚未初始化构造函数的类,则将初始化它。

      参数:
      c - 反射构造函数
      返回:
      可调用反射构造函数的方法句柄
      抛出:
      IllegalAccessException - 如果访问检查失败或者方法的可变性修饰符位被设置且asVarargsCollector失败
      NullPointerException - 如果参数为null
    • unreflectGetter

      public MethodHandle unreflectGetter(Field f) throws IllegalAccessException
      生成一个方法句柄,允许读取反射字段。方法句柄的类型将具有字段值类型的返回类型。如果字段是static,则方法句柄将不带参数。否则,它的单个参数将是包含字段的实例。如果Field对象的accessible标志未设置,则将立即代表查找类执行访问检查。

      参数:
      f - 反射字段
      返回:
      可从反射字段加载值的方法句柄
      抛出:
      IllegalAccessException - 如果访问检查失败
      NullPointerException - 如果参数为null
    • unreflectSetter

      public MethodHandle unreflectSetter(Field f) throws IllegalAccessException
      生成一个方法句柄,允许写入反射字段。方法句柄的类型将具有void返回类型。如果字段是static,则方法句柄将带有一个参数,字段值类型的值,即要存储的值。否则,两个参数将是包含字段的实例和要存储的值。如果Field对象的accessible标志未设置,则将立即代表查找类执行访问检查。 final,则不允许写入访问,并且访问检查将失败,除非在为 Field.set记录的某些狭窄情况下。仅当对应于 Field对象的 set方法的调用可以正常返回时,才会返回方法句柄。特别是,既是 static又是 final的字段可能永远不会被设置。

      static,并且如果调用返回的方法句柄,则如果尚未初始化字段的类,则将初始化它。

      参数:
      f - 反射字段
      返回:
      可将值存储到反射字段中的方法句柄
      抛出:
      IllegalAccessException - 如果访问检查失败,或者字段是final且在Field对象上未启用写入访问
      NullPointerException - 如果参数为null
    • unreflectVarHandle

      public VarHandle unreflectVarHandle(Field f) throws IllegalAccessException
      生成一个VarHandle,用于访问类型为T的反射字段f,该字段声明在类型为R的类中。VarHandle的变量类型为T。如果字段是非静态的,则VarHandle具有一个坐标类型R。否则,字段是静态的,VarHandle没有坐标类型。

      立即代表查找类执行访问检查,而不考虑字段的accessible标志的值。

      如果字段是静态的,并且对返回的VarHandle进行操作,则将初始化字段的声明类,如果尚未初始化。

      返回的VarHandle的某些访问模式在以下情况下不受支持:

      • 如果字段被声明为final,则不支持写入、原子更新、数值原子更新和位原子更新访问模式。
      • 如果字段类型不是byteshortcharintlongfloatdouble,则不支持数值原子更新访问模式。
      • 如果字段类型不是booleanbyteshortcharintlong,则不支持位原子更新访问模式。

      如果字段被声明为volatile,则返回的VarHandle将覆盖对字段的访问(实际上忽略volatile声明),以符合其指定的访问模式。

      如果字段类型为floatdouble,则数值和原子更新访问模式使用它们的位表示进行值比较(分别参见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

      public MethodHandleInfo revealDirect(MethodHandle target)
      破解由此查找对象或类似对象创建的直接方法句柄。将执行安全性和访问检查,以确保此查找对象能够重现目标方法句柄。这意味着如果目标是直接方法句柄,但是由不相关的查找对象创建的,则破解可能会失败。如果方法句柄是调用者敏感的,并且是由不同类的查找对象创建的,则可能会发生这种情况。
      参数:
      target - 要破解为符号引用组件的直接方法句柄
      返回:
      一个符号引用,可用于从此查找对象重建此方法句柄
      抛出:
      SecurityException - 如果存在安全管理器并且它拒绝访问
      IllegalArgumentException - 如果目标不是直接方法句柄或访问检查失败
      NullPointerException - 如果目标为null
      自版本:
      1.8
      另请参阅:
    • hasPrivateAccess

      @Deprecated(since="14") public boolean hasPrivateAccess()
      Deprecated.
      This method was originally designed to test PRIVATE access that implies full privilege access but MODULE access has since become independent of PRIVATE access. It is recommended to call hasFullPrivilegeAccess() instead.
      如果此查找具有PRIVATEMODULE访问权限,则返回true
      返回:
      如果此查找具有PRIVATEMODULE访问权限,则返回true
      自版本:
      9
    • hasFullPrivilegeAccess

      public boolean hasFullPrivilegeAccess()
      如果此查找具有完全特权访问,即具有PRIVATEMODULE访问权限,则返回true。为了访问查找类允许的所有成员,Lookup对象必须具有完全特权访问。
      返回:
      如果此查找具有完全特权访问,则返回true
      自版本:
      14
      另请参阅: