Java 9 中的 opens 指令
The opens directive in Java 9
我正在阅读 Java 9 规范的草案,但我不清楚这句话:
The opens directive specifies the name of a package to be opened by the current module. This makes public and protected types in the package, and their public and protected members, be accessible to code in other modules at run time only. It also makes all types in the package, and all their members, be accessible via the reflection libraries of the Java SE Platform.
如果 opens 使 public 和 protected 只能在运行时访问,那么包区域中的所有类型都可以通过反射访问是什么意思?
我不明白运行时和反射之间的区别。
打开的包似乎只能public访问并在运行时受到保护(通过反射?)以及其他未指定类型和成员的包可通过反射访问(也是私有的...)。
A package opened by a module, may be qualified or unqualified.
The opens directive in a module declaration declares a package to be
open to allow all types in the package, and all their members, not
just public types and their public members to be reflected on by APIs
that support private access or a way to bypass or suppress default
Java language access control checks.
假设您编写了一些使用库中的 public class 的代码。
import somelibrary.somepackage.SomeClass; // <-- public class from a library.
public class Main {
public static void main(String[] args) {
SomeClass.doSomething();
}
}
然后你编译这段代码,它编译得很好,因为你使用的 class 是 public
。
现在,在下一个版本的库中,包被添加到模块中,但没有导出。这意味着如果您尝试 运行 您的代码与 运行time 模块路径上的这个新模块,它会抛出异常,因为您正在尝试访问封装的包。
为了让您的代码重新工作,您可以使用命令行选项将此模块打开到您的代码中,以便它可以继续使用封装的包。
或者,库的创建者可以将 opens somepackage;
添加到库的模块定义中。这将允许您使用这个新版本运行您的代码,但不能编译。 IE。 public
和 protected
成员只能在 运行 时间访问,但不涉及反射。
当您扩展 class 并想要访问封装包中的超级 class 的 protected
成员时也是如此。
但是 opens 指令不会改变这样一个事实,即如果在下一个版本的库中创建了一个方法或字段 private
,那么如果您尝试使用它:
class SomeClass { // <-- the class in the library
public static void doSomething() {
System.out.println("doSomething"); // contrived example code
}
}
...
public class Main {
public static void main(String... args) throws Exception {
SomeClass.doSomething(); // this call compiles fine,
}
}
然后,在下一个版本的库中 doSomething
被制作成 private
:
private static void doSomething() {...}
并重新编译。但是如果你尝试 运行 旧版本的 Main
和新版本的 SomeClass
你会得到一个 IllegalAccessError
.
简而言之,opens仅对新版本库中仍然public
或protected
的成员有效。
但是,在反射的情况下,您始终可以访问 private
成员,方法是使用 setAccessible(true)
:
public static void main(String... args) throws Exception {
Method m = SomeClass.class.getDeclaredMethod("doSomething");
m.setAccessible(true);
m.invoke(null); // works Fine
}
所以在反射的情况下,打开也会使封装的私有成员再次可访问。
我正在阅读 Java 9 规范的草案,但我不清楚这句话:
The opens directive specifies the name of a package to be opened by the current module. This makes public and protected types in the package, and their public and protected members, be accessible to code in other modules at run time only. It also makes all types in the package, and all their members, be accessible via the reflection libraries of the Java SE Platform.
如果 opens 使 public 和 protected 只能在运行时访问,那么包区域中的所有类型都可以通过反射访问是什么意思?
我不明白运行时和反射之间的区别。
打开的包似乎只能public访问并在运行时受到保护(通过反射?)以及其他未指定类型和成员的包可通过反射访问(也是私有的...)。
A package opened by a module, may be qualified or unqualified.
The opens directive in a module declaration declares a package to be open to allow all types in the package, and all their members, not just public types and their public members to be reflected on by APIs that support private access or a way to bypass or suppress default Java language access control checks.
假设您编写了一些使用库中的 public class 的代码。
import somelibrary.somepackage.SomeClass; // <-- public class from a library.
public class Main {
public static void main(String[] args) {
SomeClass.doSomething();
}
}
然后你编译这段代码,它编译得很好,因为你使用的 class 是 public
。
现在,在下一个版本的库中,包被添加到模块中,但没有导出。这意味着如果您尝试 运行 您的代码与 运行time 模块路径上的这个新模块,它会抛出异常,因为您正在尝试访问封装的包。
为了让您的代码重新工作,您可以使用命令行选项将此模块打开到您的代码中,以便它可以继续使用封装的包。
或者,库的创建者可以将 opens somepackage;
添加到库的模块定义中。这将允许您使用这个新版本运行您的代码,但不能编译。 IE。 public
和 protected
成员只能在 运行 时间访问,但不涉及反射。
当您扩展 class 并想要访问封装包中的超级 class 的 protected
成员时也是如此。
但是 opens 指令不会改变这样一个事实,即如果在下一个版本的库中创建了一个方法或字段 private
,那么如果您尝试使用它:
class SomeClass { // <-- the class in the library
public static void doSomething() {
System.out.println("doSomething"); // contrived example code
}
}
...
public class Main {
public static void main(String... args) throws Exception {
SomeClass.doSomething(); // this call compiles fine,
}
}
然后,在下一个版本的库中 doSomething
被制作成 private
:
private static void doSomething() {...}
并重新编译。但是如果你尝试 运行 旧版本的 Main
和新版本的 SomeClass
你会得到一个 IllegalAccessError
.
简而言之,opens仅对新版本库中仍然public
或protected
的成员有效。
但是,在反射的情况下,您始终可以访问 private
成员,方法是使用 setAccessible(true)
:
public static void main(String... args) throws Exception {
Method m = SomeClass.class.getDeclaredMethod("doSomething");
m.setAccessible(true);
m.invoke(null); // works Fine
}
所以在反射的情况下,打开也会使封装的私有成员再次可访问。