位于上海,服务全国!

位于上海,服务全国!

理解Java 9中模块访问的复杂性

作者:admin 分类: 时间:2017-12-25 21:27:16 点击量:919


如果在模块定义文件中明确的声明了导出语句,则模块中的软件包是可访问的。 当我们想使用模块的功能时,这正是所需要的。 但是,即使没有明确的声明导出语句,我们也需要在运行时可访问模块中的所有包。 这正是打开模块和打开包关键字所需要的。 因此,当一个模块的包被导出时,其他模块只能在编译时静态访问导出包中的公共类型和这些公共类型的受保护成员。 如果我们想在运行时反射地访问一个模块呢? 本文探讨了这个模块的具体访问机制。

反射概述
反射一直是Java的重要功能之一,它使对象能够访问被指定为私有,公共或包内所保护的成员。 通过模块,Java 9引入了最高级别的封装。 因此,反射的典型访问机制也必须被重新修改,以便与模块一起工作。
一般而言,反射为软件组件提供了自行分析和在运行时动态描述其功能的能力。 例如,它使我们能够确定类支持的方法,构造函数和字段。 我们所要做的就是调用类成员的setAccessible(true)方法,如字段,方法等等。

值为true表示反射对象在使用时应该禁止Java语言访问检查。 值为false表示反射对象应强制执行Java语言访问检查。
Java通过java.base模块下的java.lang.reflect包和Class中的元素提供了此功能。 清理内存的一个简单反射示例如下。

A Quick Example
package org.mano.reflectdemo;
public class ATypicalReflectionExample {
   public static void main(String[] args) {
      String clsName = "java.math.BigInteger";
      try {
         Class cls = Class.forName(clsName);
         System.out.println(clsName + " {");
         System.out.println("// Fields");
         display (cls.getFields());
         System.out.println("// Constructors");
         display (cls.getConstructors());
         System.out.println("// Methods");
         display (cls.getMethods());
         System.out.println("}");
      } catch (ClassNotFoundException e) {
         e.printStackTrace();
      }
   }
   public static void display(Object[] obj) {
      for (int i = 0; i < obj.length; i++) {
         System.out.println("  " + obj[i]);
      }
   }
}
Output
java.math.BigInteger {
   // Fields
   public static final java.math.BigInteger
      java.math.BigInteger.ZERO
   ...
   public static final java.math.BigInteger
      java.math.BigInteger.TEN
   // Constructors
   public java.math.BigInteger(byte[],int,int)
   ...
   public java.math.BigInteger(int,int,java.util.Random)
   // Methods
   public java.math.BigInteger
      java.math.BigInteger.and(java.math.BigInteger)
   ...
   public final native void java.lang.Object.notifyAll()
}
访问规则
只有在模块中导出的包中的公共类型在编译时才可用于其它模块,或者在运行时反射。 有许多第三方库和框架严重依赖于深度反射。因此,Java 9设计者必须提供一种深度反射访问模块化代码的方法,以及一种不违反现有强封装规则的机制。 因此,模块内导出类型的访问机制按以下规范运行:
通过一个导出语句,在编译时访问包中的类型,并且通过open语句在运行时对这些类型进行深入的反射访问。
模块允许在运行时及编译时让其他模块的感兴趣读者访问公共类型,但必须在模块定义文件中明确声明包的导出。 没有明确说明导出语句的模块仍然不能被其他模块访问。 这就使用了强大的封装功能。
运行时,允许一个开放的模块深入反射特定模块中软件包中的所有类型。
未打开进行深度反射的模块称为正常模块。 但是,一些特定的软件包可以被允许在一个正常的模块中进行深度反射。 这些包被称为开放的软件包。
声明一个开放模块
声明一个开放模块很简单, 它打开深度反射的包。 语法是:

open module org.mano.services{
   // Other module statements
}
这有助于深度反射其他模块。 这意味着所有包中的类型都可以在运行时被访问。 其他声明,例如导出,要求,使用和提供声明可以以通常的方式使用。 这也意味着只有那些显式导出的包才能在编译时被访问。 因此,简而言之,一个开放修饰符只能与反射运行时可访问性相关联。
声明开放式软件包
如果我们想打开一个深度反射的包,我们必须使用一个打开语句。 打开语句不能在打开的模块声明中使用。 它有两种工作方式:

一个包内的所有类型都可以被打开以进行深度反射。 打开的包将在运行时具有完整的反射访问权限。
通过使用to子句,可以打开特定的包来访问另一个模块。 这意味着打开的软件包可以反射访问到to子句中指定的特定模块。
可以使用两个修饰符,包括其他修饰符。 下面是一个示例模块,其定义片段来说明打开包语句的构思。

module org.mano.services {
   opens org.mano.model;
   opens org.mano.drivers to com.appl.util;
   exports org.mano.model;
}
结论
Java 9带来的变化在日常编程中有许多涟漪效应。 在Java代码中可以看到许多小的和重大的变化。 首先,在模块化编程中,可访问性的概念非常重要,因为它建立了模块之间的关系。 本文试图阐明由Java 9带来的模块化编程中的反射动态可访问性。