我正在阅读Java 9规范的草案,但这句话对我来说并不清楚:
opens指令指定当前模块要打开的包的名称.这使得包中的公共和受保护类型及其公共和受保护成员只能在运行时访问其他模块中的代码.它还可以通过Java SE Platform的反射库访问包中的所有类型及其所有成员.
如果open只在运行时使public和protected可访问,那么通过反射可以访问包区域中所有类型的含义是什么?
我不明白运行时和反射之间的区别.
看起来打开的包只能在运行时访问公共和受保护(通过反射?)以及其他未指定类型的包和可访问的成员(也是私有...).
假设您编写了一些使用库中公共类的代码.
import somelibrary.somepackage.SomeClass; // <-- public class from a library.
public class Main {
public static void main(String[] args) {
SomeClass.doSomething();
}
}
Run Code Online (Sandbox Code Playgroud)
然后编译这段代码,它编译得很好,因为你正在使用的类是public.
现在,在库的下一个版本中,包将添加到模块中,但不会导出.这意味着如果您尝试在运行时模块路径上使用此新模块运行代码,则会因为您尝试访问封装的包而引发异常.
为了使代码重新运行,您可以使用命令行选项将此模块打开到您的代码,以便它可以继续使用封装的包.
或者,库的创建者可以添加opens somepackage;到库的模块定义.这将允许您使用此新版本运行代码,但不能使用它进行编译.即public与protected成员只能在运行时可以访问,但没有反映参与.
当您扩展一个类,并且想要访问protected封装包中的超类的成员时,也是如此.
但是open指令并没有改变这样一个事实:如果在库的下一个版本中创建了一个方法或字段private,那么IllegalAccessError如果你尝试使用它,你会得到一个:
class SomeClass { // <-- the class in the library
public static void doSomething() {
System.out.println("doSomething"); // contrived example code
}
}
Run Code Online (Sandbox Code Playgroud)
...
public class Main {
public static void main(String... args) throws Exception {
SomeClass.doSomething(); // this call compiles fine,
}
}
Run Code Online (Sandbox Code Playgroud)
然后,在图书馆的下一个版本doSomething是由private:
private static void doSomething() {...}
Run Code Online (Sandbox Code Playgroud)
并重新编译.但是,如果你尝试运行旧版本Main的新版本,SomeClass你得到一个IllegalAccessError.
简而言之,打开仅适用于仍在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
}
Run Code Online (Sandbox Code Playgroud)
因此,在反射的情况下,打开也会使封装的私有成员再次可访问.