Module  java.base
软件包  java.lang.invoke

Class MethodHandles.Lookup

  • Enclosing class:
    MethodHandles


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

    需要创建方法句柄的查找类将调用MethodHandles.lookup为自己创建一个工厂。 当创建Lookup工厂对象时,确定查找类的标识,并将其安全地存储在Lookup对象中。 查找类(或其Lookup )可以使用Lookup对象上的工厂方法来创建访问检查成员的方法句柄。 这包括允许查找类的所有方法,构造函数和字段,甚至是私有的。

    查找工厂方法

    Lookup对象上的工厂方法对应于方法,构造函数和字段的所有主要用例。 由工厂方法创建的每个方法句柄都是特定字节码行为的功能等价物。 (字节码行为在Java虚拟机规范的5.4.3.5节中描述。)这是这些工厂方法与生成的方法句柄的行为之间的对应关系的总结: lookup method behaviors lookup expression member bytecode behavior lookup.findGetter(C.class,"f",FT.class) FT f; (T) this.f; lookup.findStaticGetter(C.class,"f",FT.class) static
    FT f; (T) 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.unreflect(aMethod) (static)?
    T m(A*); (T) aMethod.invoke(thisOrNull, 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 名称aMethodaFieldaConstructor支架对应于给定的成员反射物体。

    findClass操作的字节码行为是一个常量类的负载,好像通过ldc CONSTANT_Class 行为被表示,而不是方法句柄,而是直接作为一个Class常量。

    在给定成员具有可变权限(即,方法或构造方法)的情况下,返回的方法句柄也将为variable arity 在所有其他情况下,返回的方法句柄将是固定的。

    讨论:查找方法句柄和底层类成员和字节码行为之间的等价性可以通过以下几种方法分解:

    • 如果C不能从查找类的加载器中进行符号访问,即使没有等效的Java表达式或字节码常量,查找仍然可以成功。
    • 同样,如果TMT不能从查找类的加载器中进行符号访问,则查找仍然可以成功。 例如,无论请求类型如何, MethodHandle.invokeExactMethodHandle.invoke查找将始终成功。
    • 如果安装了一个安全管理器,它可以禁止在各种情况下查找( see below )。 相比之下, ldc指令对CONSTANT_MethodHandle常数不进行安全管理员检查。
    • 如果查找方法有一个very large arity ,方法句柄创建可能会失败,因为方法句柄类型具有太多的参数。

    访问检查

    当创建方法句柄时,访问检查应用于工厂方法Lookup 这是与Core Reflection API的关键区别,因为java.lang.reflect.Method.invoke对每个呼叫者进行每次呼叫的访问检查。

    所有访问检查从Lookup对象开始,它将其记录的查找类与所有请求进行比较,以创建方法句柄。 单个Lookup对象可用于创建任意数量的访问检查方法句柄,所有这些都针对单个查找类进行检查。

    一个Lookup对象可以与其他可信代码共享,如元对象协议。 共享的Lookup对象委派了在查找类的私有成员上创建方法句柄的功能。 即使特权代码使用Lookup对象,访问检查仅限于原始查找类的权限。

    查找可能会失败,因为查找类不能访问包含的类,或者因为缺少所需的类成员,或者因为查找类不能访问所需的类成员,或者因为查找对象不够信任访问会员 在任何这些情况下,将从尝试查找中抛出ReflectiveOperationException 确切的课程将是以下之一:

    • NoSuchMethodException - 如果请求但不存在的方法
    • NoSuchFieldException - 如果一个字段被请求但不存在
    • IllegalAccessException - 如果成员存在,但访问检查失败

    通常,可以查找方法方法M的方法句柄的条件不比查找类可以编译,验证和解析到M的调用的条件更多的限制。 JVM会引发像NoSuchMethodError这样的NoSuchMethodError ,方法句柄查找通常会引发相应的检查异常,如NoSuchMethodException 并且调用由查找产生的方法句柄的效果是exactly equivalent执行编译,验证和解析的调用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

    在某些情况下,Java编译器可以通过创建包装方法来访问同一顶层声明中另一个类的私有方法来获取嵌套类之间的访问。 例如,一个嵌套类C.D可以其他相关的类如内访问私有成员CC.D.E ,或C.B ,但是Java编译器可能需要生成在这些相关的类包装方法。 在这种情况下,一个Lookup的对象C.E将无法将这些私有成员。 此限制的解决方法是Lookup.in方法,可以将C.E的查找C.E为任何其他类中的查找,而不特别提升权限。

    允许给定查找对象的访问可以根据其集合lookupModes限制到查找类通常可访问的成员的子集。 例如, publicLookup方法生成一个查找对象,该对象只允许访问导出包的公共类中的公共成员。 调用者敏感方法lookup产生一个查找对象,具有相对于其调用者类的完整功能,以模拟所有支持的字节码行为。 此外, Lookup.in方法可能产生一个查找对象,其访问模式比原始查找对象少。

    私人访问 讨论:如果lookup modes包含访问private成员的可能性,我们认为查找具有私有访问权限 如其他相关方法中所述,只有具有私有访问权限的查询具有以下功能:

    这些权限中的每一个都是这样一个事实的结果:具有私有访问的查找对象可以安全地追溯到一个始发类,其起始类可以可靠地确定其bytecode behaviors和Java语言访问权限并通过方法句柄进行仿真。

    安全管理器交互

    虽然字节码指令只能引用相关类加载器中的类,但是该API可以搜索任何类中的方法,只要对其Class对象的引用可用即可。 Core Reflection API也可以使用这种跨装载程序引用,并且不可能使用诸如invokestaticgetfield字节码指令。 有一个security manager API允许应用程序检查这样的跨装载程序引用。 这些检查适用于MethodHandles.Lookup API和Core Reflection API(见Class )。

    如果存在安全管理员,则成员和类查找需要额外的检查。 从一到三个电话是安全管理员。 任何这些电话可以通过抛出一个SecurityException拒绝访问。 限定smgr作为安全管理器, lookc作为查找类的当前查找对象,的refc作为其中成员正在寻求包含类,和defc作为其中构件被实际定义的类。 (如果一个类或其他类型的被存取时, refcdefc值是类本身。)的值lookc被定义为不存在 ,如果当前查找对象不具有private access 呼叫是按照以下规则进行的:

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

    来电者敏感方法

    少量的Java方法有一个特殊的属性叫做呼叫者敏感性。 来电者敏感的方法可以根据其直接呼叫者的身份而有所不同。

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

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

    讨论:例如,呼叫者敏感的方法Class.forName(x)可以返回不同的类或抛出不同的异常,这取决于调用它的类的类加载器。 Class.forName的公开查询将失败,因为没有合理的方式来确定其字节码行为。

    如果应用程序缓存方法句柄进行广泛共享,则应使用publicLookup()创建它们。 如果有一个Class.forName的查找,它将失败,并且应用程序必须在这种情况下采取适当的措施。 可能是在引用引导方法的调用期间的后续查找可以包含调用者的特定身份,从而使该方法可访问。

    功能MethodHandles.lookup是调用者敏感的,因此可以有一个用于查找的安全基础。 JSR 292 API中几乎所有其他方法都依赖于查找对象来检查访问请求。

    • 字段详细信息

      • MODULE

        public static final int MODULE
        代表module访问(默认访问)的单位掩码,这可能有助于lookupModes的结果。 该值为0x10 ,与任何特定的modifier bit无关 结合PUBLIC修饰符位,具有此查找模式的Lookup可以访问查找类的模块中的所有公共类型以及其他模块导出的包中的公共类型到查找类的模块。
        从以下版本开始:
        9
        另请参见:
        Constant Field Values
    • 方法详细信息

      • lookupClass

        public Class<?> lookupClass​()
        告诉哪个类执行查找。 正是这种针对哪些检查执行可见性和访问权限的类。

        该类意味着访问许可的最大级别,但是该权限可能另外受到位掩码lookupModes限制,该位掩码控制是否可以访问非公共成员。

        结果
        查找类,代表此查找对象查找成员
      • lookupModes

        public int lookupModes​()
        告诉这个查找对象可以产生哪些访问保护类的成员。 结果是比特的位掩码PUBLIC (0x01)PRIVATE (0x02)PROTECTED (0x04)PACKAGE (0x08)MODULE (0x10) ,和UNCONDITIONAL (0x20)

        caller's class上新创建的查找对象具有所有可能的位,除了UNCONDITIONAL 查找可用于访问调用者类的所有成员,调用者模块中的所有公共类型以及其他模块导出到调用者模块的包中的所有公共类型。 新查找类created from a previous lookup object上的查找对象可能将某些模式位设置为零。 模式位也可以是directly cleared 一旦清除,模式位不能从降级的查找对象恢复。 这样做的目的是通过新的查找对象限制访问,以便它只能访问原始查找对象可以访问的名称,也可以访问新的查找类。

        结果
        查找模式,限制由该查找对象执行的访问的种类
        另请参见:
        in(java.lang.Class<?>)dropLookupMode(int)
      • in

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

        但是,所得到的Lookup对象保证没有比原来更多的访问能力。 特别是,访问功能可能会丢失如下:

        • 如果旧查找类在一个named模块中,并且新的查找类位于不同的模块M ,则M的导出包中的任何成员甚至公共成员也不可访问。 这个例外是当这个查找是publicLookup ,在这种情况下, PUBLIC访问不会丢失。
        • 如果旧的查找类在一个未命名的模块中,并且新的查找类是不同的模块,那么MODULE访问将丢失。
        • 如果新的查找类与旧的查找类不同,则丢失UNCONDITIONAL
        • 如果新的查找类与旧的查找类不同,则保护和默认(包)成员将无法访问。
        • 如果新的查找类与旧的查找类不在同一个包成员内,则私有成员将无法访问,受保护的成员将无法通过继承访问。 (受保护的成员可能因为包的共享而可以访问。)
        • 如果旧的查找类不能访问新的查找类,那么任何成员,甚至公共成员都不可访问。 (在所有其他情况下,公共成员将继续可访问。)

        结果查找的加载类(在findClass(java.lang.String)调用期间使用)的功能由查找类'加载器确定,这可能由于此操作而改变。

        参数
        requestedLookupClass - 新查找对象所需的查找类
        结果
        报告所需查找类的查找对象,如果没有变化则是相同的对象
        异常
        NullPointerException - 如果参数为空
      • dropLookupMode

        public MethodHandles.Lookup dropLookupMode​(int modeToDrop)
        在同一查找类中创建一个查找,该查找对象查找成员,但查找模式已丢失给定的查找模式。 查找模式下降是一个PUBLICMODULEPACKAGEPROTECTEDPRIVATE PROTECTEDUNCONDITIONAL总是丢弃,因此所得到的查找模式将永远不具有这些访问功能。 当丢弃PACKAGE ,所得到的查找将不具有PACKAGEPRIVATE访问权限。 当拖放MODULE然后将得到的查找不会有MODULEPACKAGE ,或PRIVATE访问。 如果PUBLIC被删除,则生成的查找将无法访问。
        参数
        modeToDrop - 查找模式下降
        结果
        缺少指示模式的查找对象,如果没有变化则是相同的对象
        异常
        IllegalArgumentException -如果 modeToDrop不是一个 PUBLICMODULEPACKAGEPROTECTEDPRIVATEUNCONDITIONAL
        从以下版本开始:
        9
        另请参见:
        MethodHandles.privateLookupIn(java.lang.Class<?>, java.lang.invoke.MethodHandles.Lookup)
      • toString

        public String toString​()
        显示要从中进行查找的类的名称。 (该名称是由Class.getName报告的。)如果对该查找允许的访问有限制,则通过在类名称中添加一个后缀(由斜杠和关键字组成)来指示。 该关键字表示最强的访问权限,选择如下:
        • 如果不允许访问,则后缀为“/ noaccess”。
        • 如果只允许对导出包中的类型进行公共访问,则后缀为“/ public”。
        • 如果只允许公共访问和无条件访问,则后缀为“/ publicLookup”。
        • 如果只允许公共和模块访问,则后缀为“/ module”。
        • 如果只允许公共,模块和包访问,则后缀为“/ package”。
        • 如果只允许public,module,package和private访问,则后缀为“/ private”。
        如果没有上述情况适用,则允许完全访问(public,module,package,private和protected)的情况。 在这种情况下,不添加后缀。 这只是原始从MethodHandles.lookup获得的对象。 Lookup.in创建的对象总是有限制访问,并显示一个后缀。

        (看起来很奇怪,受保护的访问应该比私有访问更强大,独立于包访问,受保护访问是首先丢失的,因为它需要调用者和被调用方之间的直接子类关系。

        重写:
        toStringObject
        结果
        对象的字符串表示形式。
        另请参见:
        in(java.lang.Class<?>)
      • findStatic

        public MethodHandle findStatic​(Class<?> refc,
                                       String name,
                                       MethodType type)
                                throws NoSuchMethodException,
                                       IllegalAccessException
        为静态方法生成方法句柄。 方法句柄的类型将是该方法的类型。 (由于静态方法不接收接收器,所以在方法句柄类型中没有插入附加的接收器参数,因为将使用findVirtualfindSpecial 。查找对象的方法及其所有参数类型必须可访问。

        当且仅当方法的变量修饰符位( 0x0080 )被设置时,返回的方法句柄将具有variable arity

        如果调用返回的方法句柄,方法的类将被初始化,如果尚未被初始化。

        例:

        
        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 - 如果一个安全管理员存在,它是 refuses access
        NullPointerException - 如果任何参数为空
      • findVirtual

        public MethodHandle findVirtual​(Class<?> refc,
                                        String name,
                                        MethodType type)
                                 throws NoSuchMethodException,
                                        IllegalAccessException
        为虚拟方法生成方法句柄。 方法句柄的类型将是该方法的类型,接收器类型(通常为refc )为前缀。 查询对象必须可访问该方法及其所有参数类型。

        当被调用时,句柄会将第一个参数视为接收者,并在接收者的类型上进行调度,以确定要输入的方法实现。 (调度动作与invokevirtualinvokeinterface指令执行的动作相同)

        如果查找类具有访问该成员的完整权限,则第一个参数将为refc 否则该成员必须是protected并且第一个参数将被限制在查找类的类型中。

        返回的方法句柄将会有variable arity,如果且仅当方法的变量arity修饰符位( 0x0080 )被设置。

        由于由findVirtual生成的invokevirtual指令和方法句柄之间的一般equivalence ,如果类为MethodHandle ,名称字符串为invokeExactinvoke ,则生成的方法句柄相当于由MethodHandles.exactInvokerMethodHandles.invoker生成的相同的type参数。

        如果类是VarHandle ,并且名称字符串对应于签名多态访问模式方法的名称,则生成的方法句柄等效于由MethodHandles.varHandleInvoker(java.lang.invoke.VarHandle.AccessMode, java.lang.invoke.MethodType)生成的访问模式对应于名称字符串并具有相同的type参数的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 ,或者方法是 private接口方法,或者方法的变量arity修饰符位置位,并且 asVarargsCollector失败
        SecurityException - 如果安全管理员存在,它是 refuses access
        NullPointerException - 如果任何参数为空
      • findConstructor

        public MethodHandle findConstructor​(Class<?> refc,
                                            MethodType type)
                                     throws NoSuchMethodException,
                                            IllegalAccessException
        使用指定类型的构造函数生成创建对象并初始化它的方法句柄。 方法句柄的参数类型将是构造函数的参数类型,而返回类型将是对构造函数类的引用。 查询对象必须可以访问构造函数及其所有参数类型。

        请求的类型必须具有void的返回类型。 (这与JVM处理构造函数类型描述符一致。)

        当且仅当构造函数的变量arity修饰符位( 0x0080 )被设置时,返回的方法句柄将具有variable arity

        如果调用返回的方法句柄,那么构造函数的类将被初始化,如果还没有被初始化。

        例:

        
        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 - 如果安全管理员存在,它是 refuses access
        NullPointerException - 如果任何参数为空
      • accessClass

        public Class<?> accessClass​(Class<?> targetClass)
                             throws IllegalAccessException
        确定是否可以从此Lookup对象定义的查找上下文中访问类。 该类的静态初始化程序未运行。

        这里的查找上下文由lookup classlookup modes确定。

        参数
        targetClass - 要进行访问检查的类
        结果
        已经访问检查的类
        异常
        IllegalAccessException - 如果类不能从查找类访问,则使用允许的访问模式。
        SecurityException - 如果一个安全管理器存在,它是 refuses access
        从以下版本开始:
        9
      • findSpecial

        public MethodHandle findSpecial​(Class<?> refc,
                                        String name,
                                        MethodType type,
                                        Class<?> specialCaller)
                                 throws NoSuchMethodException,
                                        IllegalAccessException
        为虚拟方法生成早期绑定的方法句柄。 它会绕过检查重写在接收机,方法as if calledinvokespecial从明确内指令指定specialCaller 方法句柄的类型将是该方法的类型,前面加上适当受限的接收器类型。 (接收器类型将为specialCaller或子类型。)查找对象必须可访问该方法及其所有参数类型。

        在方法解析之前,如果显式指定的调用者类与lookup类不相同,或者该查找对象没有private access权限,则访问失败。

        当且仅当方法的变量arity修饰符位( 0x0080 )被设置时,返回的方法句柄将具有variable arity

        (注:即使在特殊情况下, invokespecial指令可以引用它们,这个API也不可见JVM内部方法,名称为"<init>" ,使用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(); }
        }
        ...
        // 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 - 如果安全管理员存在,它是 refuses access
        NullPointerException - 如果任何参数为空
      • findVarHandle

        public VarHandle findVarHandle​(Class<?> recv,
                                       String name,
                                       Class<?> type)
                                throws NoSuchFieldException,
                                       IllegalAccessException
        产生一个VarHandle,可访问name类中声明的类型为type的非静态字段recv VarHandle的变量类型是type ,它有一个坐标类型, recv

        代表查找类立即执行访问检查。

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

        • 如果该字段声明为final ,则不支持写入,原子更新,数字原子更新和逐位原子更新访问模式。
        • 如果字段的类型是比其他任何byteshortcharintlongfloat ,或double然后数字原子更新的接入方式是不受支持的。
        • 如果字段的类型是以外的任何其他booleanbyteshortcharintlong然后逐位原子更新的接入方式是不受支持的。

        如果该字段声明为volatile则返回的VarHandle将根据其指定的访问模式覆盖对该字段的访问(有效地忽略volatile声明)。

        如果字段类型为floatdouble则数字和原子更新访问模式使用其逐位表示比较值(分别为Float.floatToRawIntBits(float)Double.doubleToRawLongBits(double) )。

        API Note:
        由数字和原子更新访问模式执行的float值或double值的float比较不同于原语==运算符和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 - 如果一个安全管理员存在,它是 refuses access
        NullPointerException - 如果任何参数为空
        从以下版本开始:
        9
      • findStaticGetter

        public MethodHandle findStaticGetter​(Class<?> refc,
                                             String name,
                                             Class<?> type)
                                      throws NoSuchFieldException,
                                             IllegalAccessException
        产生一个给静态字段读取访问权限的方法句柄。 方法句柄的类型将具有字段值类型的返回类型。 方法句柄将不会引用任何参数。 代表查找类立即执行访问检查。

        如果调用了返回的方法句柄,则该字段的类将被初始化,如果尚未初始化。

        参数
        refc - 访问该方法的类或接口
        name - 该字段的名称
        type - 字段的类型
        结果
        一个可以从字段加载值的方法句柄
        异常
        NoSuchFieldException - 如果该字段不存在
        IllegalAccessException - 如果访问检查失败,或者该字段不是 static
        SecurityException - 如果一个安全管理员存在,它是 refuses access
        NullPointerException - 如果任何参数为空
      • findStaticSetter

        public MethodHandle findStaticSetter​(Class<?> refc,
                                             String name,
                                             Class<?> type)
                                      throws NoSuchFieldException,
                                             IllegalAccessException
        生成方法句柄,给静态字段写权限。 方法句柄的类型将具有void返回类型。 方法句柄将使用单个参数,字段的值类型,要存储的值。 代表查找类立即执行访问检查。

        如果调用了返回的方法句柄,则该字段的类将被初始化,如果尚未初始化。

        参数
        refc - 访问该方法的类或接口
        name - 该字段的名称
        type - 字段的类型
        结果
        一个可以将值存储到字段中的方法句柄
        异常
        NoSuchFieldException - 如果该字段不存在
        IllegalAccessException - 如果访问检查失败,或者该字段不是 static
        SecurityException - 如果一个安全管理员存在,它是 refuses access
        NullPointerException - 如果任何参数为空
      • findStaticVarHandle

        public VarHandle findStaticVarHandle​(Class<?> decl,
                                             String name,
                                             Class<?> type)
                                      throws NoSuchFieldException,
                                             IllegalAccessException
        产生一个VarHandle,可以访问name类中声明的类型为type的静态字段decl VarHandle的变量类型是type ,它没有坐标类型。

        代表查找类立即执行访问检查。

        如果返回的VarHandle操作,则声明类将被初始化,如果尚未初始化。

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

        • 如果该字段声明为final ,则不支持写入,原子更新,数字原子更新和按位原子更新访问模式。
        • 如果字段的类型是比其他任何byteshortcharintlongfloat ,或double ,然后数字原子更新的接入方式是不受支持的。
        • 如果字段的类型是以外的任何其他booleanbyteshortcharintlong然后逐位原子更新的接入方式是不受支持的。

        如果该字段声明为volatile则返回的VarHandle将根据其指定的访问模式覆盖该字段的访问(有效地忽略volatile声明)。

        如果字段类型为floatdouble则数字和原子更新访问模式使用其逐位表示比较值(分别见Float.floatToRawIntBits(float)Double.doubleToRawLongBits(double) )。

        API Note:
        按照数字和原子更新访问模式执行的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 - 如果一个安全管理员存在,它是 refuses access
        NullPointerException - 如果任何参数为空
        从以下版本开始:
        9
      • bind

        public MethodHandle bind​(Object receiver,
                                 String name,
                                 MethodType type)
                          throws NoSuchMethodException,
                                 IllegalAccessException
        为非静态方法生成早期绑定的方法句柄。 接收者必须有一个超类型defc ,其中给定名称和类型的方法可以被查找类访问。 查询对象必须可访问该方法及其所有参数类型。 方法句柄的类型将是该方法的类型,而不插入附加的接收器参数。 给定的接收器将被绑定到方法句柄中,以便每次调用方法句柄将在给定的接收器上调用请求的方法。

        如果且仅当方法的变量arity修饰符位( 0x0080 )被设置尾数组参数不是唯一的参数时,返回的方法句柄将具有variable arity (如果尾数组参数是唯一的参数,给定的接收器值将被绑定到它。)

        这几乎等同于以下代码,下面有一些区别:

        
        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()receiver.getClass()的超类型,其中所请求的方法可以被查找类访问。 (不像bindbindTo不保留变量元数。另外, bindTo可能抛出ClassCastException中的实例,其中bind将引发IllegalAccessException ,如在其中所述构件是所述壳体protected和接收器通过限制findVirtual到查找类)。
        参数
        receiver - 访问该方法的对象
        name - 方法的名称
        type - 方法的类型,省略了接收方参数
        结果
        所需的方法句柄
        异常
        NoSuchMethodException - 如果方法不存在
        IllegalAccessException - 如果访问检查失败,或者方法的变量 asVarargsCollector修饰符位置位,并且 asVarargsCollector失败
        SecurityException - 如果安全管理器存在,它是 refuses access
        NullPointerException - 如果任何参数为空
        另请参见:
        MethodHandle.bindTo(java.lang.Object)findVirtual(java.lang.Class<?>, java.lang.String, java.lang.invoke.MethodType)
      • unreflect

        public MethodHandle unreflect​(方法 m)
                               throws IllegalAccessException
        做一个direct method handlem ,如果查找类有权限。 如果m是非静态的,则接收者参数被视为初始参数。 如果m是虚拟的,则在每次呼叫时都应该重写。 与Core Reflection API不同,异常包装。 方法句柄的类型将是方法的类型,接收器类型为前缀(但仅当它是非静态的)时。 如果方法的accessible标志未设置,则代表查找类立即执行访问检查。 如果m不是公开的,不要与不信任方共享所产生的句柄。

        当且仅当方法的变量arity修饰符位( 0x0080 )被设置时,返回的方法句柄将具有variable arity

        如果m是静态的,并且如果返回的方法句柄被调用,那么方法的类将被初始化,如果它还没有被初始化。

        参数
        m - 反映的方法
        结果
        可以调用反射方法的方法句柄
        异常
        IllegalAccessException - 如果访问检查失败,或者如果方法的变量arity修饰符位被设置并且 asVarargsCollector失败
        NullPointerException - 如果参数为空
      • unreflectSpecial

        public MethodHandle unreflectSpecial​(方法 m,
                                             Class<?> specialCaller)
                                      throws IllegalAccessException
        为反映的方法生成方法句柄。 它会绕过检查重写在接收机,方法as if calledinvokespecial从明确内指令指定specialCaller 方法句柄的类型将是该方法的类型,前面加上适当受限的接收器类型。 (接收器类型为specialCaller或子类型。)如果未设置方法的accessible标志,则代表查找类立即执行访问检查,就像invokespecial指令被链接一样。

        在方法解析之前,如果显式指定的调用者类与lookup类不相同,或者该查找对象没有private access权限,则访问失败。

        如果且仅当方法的变量arity修饰符位( 0x0080 )被设置时,返回的方法句柄将具有variable arity

        参数
        m - 反映的方法
        specialCaller - 这个类 specialCaller叫做方法
        结果
        可以调用反射方法的方法句柄
        异常
        IllegalAccessException - 如果访问检查失败,或者方法为 static ,或者方法的变量arity修饰符位是否设置并且 asVarargsCollector发生故障
        NullPointerException - 如果任何参数为空
      • unreflectConstructor

        public MethodHandle unreflectConstructor​(Constructor<?> c)
                                          throws IllegalAccessException
        为反射的构造函数生成方法句柄。 方法句柄的类型将是构造函数的类型,返回类型更改为声明类。 方法句柄将执行一个newInstance操作,在传递给方法句柄的参数上创建构造函数类的新实例。

        如果未设置构造函数的accessible标志,则代表查找类立即执行访问检查。

        当且仅当构造函数的变量arity修饰符位( 0x0080 )被设置时,返回的方法句柄将具有variable arity

        如果调用返回的方法句柄,那么构造函数的类将被初始化,如果还没有被初始化。

        参数
        c - 反映的构造函数
        结果
        可以调用反射的构造函数的方法句柄
        异常
        IllegalAccessException - 如果访问检查失败或方法的变量修改位置1,并且 asVarargsCollector失败
        NullPointerException - 如果参数为空
      • unreflectGetter

        public MethodHandle unreflectGetter​(Field f)
                                     throws IllegalAccessException
        产生方法句柄,给予反映字段的读访问权限。 方法句柄的类型将具有字段值类型的返回类型。 如果该字段是静态的,方法句柄将不会引用任何参数。 否则,其单个参数将是包含该字段的实例。 如果字段的accessible标志未设置,则代表查找类立即执行访问检查。

        如果该字段是静态的,并且如果返回的方法句柄被调用,则该字段的类将被初始化,如果它还没有被初始化。

        参数
        f - 反射场
        结果
        一个方法句柄,可以从反射的字段加载值
        异常
        IllegalAccessException - 如果访问检查失败
        NullPointerException - 如果参数为空
      • unreflectSetter

        public MethodHandle unreflectSetter​(Field f)
                                     throws IllegalAccessException
        产生一个方法句柄,给一个反射的字段写权限。 方法句柄的类型将具有void返回类型。 如果该字段是静态的,那么方法句柄将使用一个参数,即字段的值类型,要存储的值。 否则,两个参数将是包含该字段的实例和要存储的值。 如果字段的accessible标志未设置,则代表查找类立即执行访问检查。

        如果该字段是静态的,并且如果返回的方法句柄被调用,则该字段的类将被初始化,如果它还没有被初始化。

        参数
        f - 反映的领域
        结果
        一种可以将值存储到反射场中的方法句柄
        异常
        IllegalAccessException - 如果访问检查失败
        NullPointerException - 如果参数为null
      • unreflectVarHandle

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

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

        如果该字段是静态的,并且如果返回的VarHandle被操作,则该字段的声明类将被初始化,如果它还没有被初始化。

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

        • 如果该字段声明为final ,则不支持写入,原子更新,数字原子更新和逐位原子更新访问模式。
        • 如果字段的类型是比其他任何byteshortcharintlongfloat ,或double然后数字原子更新的接入方式是不受支持的。
        • 如果字段的类型是以外的任何其他booleanbyteshortcharintlong然后逐位原子更新的接入方式是不受支持的。

        如果该字段声明为volatile则返回的VarHandle将根据其指定的访问模式覆盖该字段的访问(有效地忽略volatile声明)。

        如果字段类型为floatdouble则数字和原子更新访问模式使用其逐位表示比较值(分别见Float.floatToRawIntBits(float)Double.doubleToRawLongBits(double) )。

        API Note:
        由数字和原子更新访问模式执行的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 - 如果参数为空
        从以下版本开始:
        9
      • revealDirect

        public MethodHandleInfo revealDirect​(MethodHandle target)
        破解由此查找对象或类似的对象创建的一个direct method handle 执行安全和访问检查以确保该查找对象能够再现目标方法句柄。 这意味着如果目标是一个直接的方法句柄但是由一个不相关的查找对象创建,则破解可能会失败。 如果方法句柄是caller sensitive,并且由不同类的查找对象创建,则可能会发生这种情况。
        参数
        target - 一种直接的方法手柄来破解符号参考组件
        结果
        一个符号引用,可用于从此查找对象重建此方法句柄
        异常
        SecurityException - 如果一个安全管理器存在,它是 refuses access
        IllegalArgumentException - 如果目标不是直接方法句柄或访问检查失败
        NullPointerException - 如果目标是 null
        从以下版本开始:
        1.8
        另请参见:
        MethodHandleInfo
      • hasPrivateAccess

        public boolean hasPrivateAccess​()
        如果此查找具有 PRIVATE访问权限,则返回 true
        结果
        true如果这个查找有 PRIVATE访问。
        从以下版本开始:
        9