Java 简明教程

Java - Hidden Classes

Java 15 引入了 hidden classes,其他类的字节码无法直接使用该 hidden classes。此 hidden classes 供在运行时生成类并使用反射使用该类的框架使用。

hidden class 被定义为基于访问控制上下文的一个成员,可以独立于其他类卸载。

此提案 JEP 371 旨在通过提供一个标准 API 来定义无法发现且生命周期有限的 hidden classes ,从而改进 JVM 上的所有语言。JDK 框架或外部框架可以动态生成类,这些类可以生成隐藏的类。

JVM 语言严重依赖动态类生成来实现灵活性和效率。

Goals

以下是此增强功能针对的目标目标列表:

  1. 框架应该能够将类定义为框架的不可发现实施细节,这些类无法链接到其他类,也无法使用反射发现。

  2. 使用不可发现类扩展访问控制巢。

  3. 隐藏类的主动卸载将帮助框架在不降低性能的情况下尽可能多地定义隐藏类。

  4. 弃用非标准的 API,即 misc.Unsafe::defineAnonymousClass,将在未来版本中删除。

Creating a Hidden Class

为了创建一个隐藏类,我们必须创建一个查找实例,如下所示:

MethodHandles.Lookup lookup = MethodHandles.lookup();

一旦查找实例可用,我们就可以使用 defineHiddenClass() 方法来使用隐藏类的字节数组创建隐藏类。

 Class<?> hiddenClass = lookup.defineHiddenClass(getByteArray(), true, ClassOption.NESTMATE).lookupClass();

可以通过隐藏类的类路径来获取隐藏类的字节数组。

public static byte[] getByteArray() throws IOException {
   InputStream stream = Util.class.getClassLoader().getResourceAsStream("com/tutorialspoint/Util.class");
   byte[] bytes = stream.readAllBytes();
   return bytes;
}

加载 hiddenClass 类后,我们就可以使用 getConstructor() 方法创建其实例。

 Object hiddenClassObj = hiddenClass.getConstructor().newInstance();

使用隐藏类实例,我们可以获取方法,然后执行该方法,如下所示:

Method method = hiddenClassObj.getClass().getDeclaredMethod("square", Integer.class);
// call the method and get result
Object result = method.invoke(hiddenClassObj, 3);

由于隐藏类是隐藏的,无法使用反射进行实例化,因此其隐藏属性为 true,规范名称为 null。

Example to Create and Use a Hidden Class

以下示例展示了隐藏类的创建和使用。首先,我们根据如下所示创建了一个公共类 Util:

package com.tutorialspoint;

public class Util {
   public Integer square(Integer n) {
      return n * n;
   }
}

现在,我们已将此类创建为一个隐藏类,并使用其方法来获取数字的平方。

package com.tutorialspoint;

import java.io.IOException;
import java.io.InputStream;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup.ClassOption;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Tester {
   public static void main(String args[]) throws IllegalAccessException, IOException, InstantiationException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
      // create the lookup object
      MethodHandles.Lookup lookup = MethodHandles.lookup();
      // define the hidden class using the byte array of Util class
      // Using NESTMATE option so that hidden class has access to
      // private members of classes in same nest
      Class<?> hiddenClass = lookup.defineHiddenClass(getByteArray(),
         true, ClassOption.NESTMATE).lookupClass();

      // get the hidden class object
      Object hiddenClassObj = hiddenClass.getConstructor().newInstance();

      // get the hidden class method
      Method method = hiddenClassObj.getClass().getDeclaredMethod("square", Integer.class);

      // call the method and get result
      Object result = method.invoke(hiddenClassObj, 3);

      // print the result
      System.out.println(result);

      // as hidden class is not visible to jvm, it will print hidden
      System.out.println(hiddenClass.isHidden());

      // canonical name is null thus this class cannot be instantiated using reflection
      System.out.println(hiddenClass.getCanonicalName());

   }
   public static byte[] getByteArray() throws IOException {
      InputStream stream = Util.class.getClassLoader().getResourceAsStream("com/tutorialspoint/Util.class");
      byte[] bytes = stream.readAllBytes();
      return bytes;
   }
}

让我们编译并运行上述程序,这将生成以下结果 −

9
true
null