Java 简明教程

Java - Hidden Classes

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

Java 15 has introduced hidden classes which cannot be used directly by other classes bytecode. These hidden classes are intended to be used by frameworks that generate classes at runtime and use them using reflection.

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

A hidden class is defined as a member of the Based Access Control Context and it can be unloaded irrespective of other classes.

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

This proposal, JEP 371, aims at the improvement of all languages on JVM by providing a standard API to define hidden classes that are not discoverable and have a limited lifecycle. JDK frameworks or external frameworks can generate classes dynamically which can generate hidden classes.

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

JVM languages rely heavily on dynamic class generation for flexibility and efficiency.

Goals

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

Following is the list of targetted goals of this enhancement:

  1. Frameworks should be able to define classes as non-discoverable implementation details of the framework, These classes can neither be linked to other classes nor discoverable using reflection.

  2. Extend Access Control Nest with non-discoverable classes.

  3. Aggressive unloading of hidden classes will help frameworks to define as many hidden classes as required without degrading the performance.

  4. Deprecate the non-standard API, misc.Unsafe::defineAnonymousClass, to be removed in future releases.

Creating a Hidden Class

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

In order to create a hidden class, we must create a Lookup instance as shown below:

MethodHandles.Lookup lookup = MethodHandles.lookup();

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

Once lookup instance is available, we can use defineHiddenClass() method to create hidden class using bytearray of hidden class.

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

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

Bytearray of hidden class can be retrieved using classpath of the hidden class.

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

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

Once hiddenClass class is loaded, we can create its instance using getConstructor() method.

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

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

Using hidden class instance, we can get the method and then execute it as shown below:

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

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

As hidden class is hidden and cannot be instantiated using reflection, its hidden property is true and canonical name is null.

Example to Create and Use a Hidden Class

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

Following example shows the creation and use of a hidden class. First we’ve created a public class Util as shown below:

package com.tutorialspoint;

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

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

Now we’ve created this class as a hidden class and accesed its method to get the square of a number.

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;
   }
}

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

Let us compile and run the above program, this will produce the following result −

9
true
null