Java 14 Jigsaw 反射到非导出包中的 class 不拒绝访问

Java 14 Jigsaw reflection to a class in not a Exported package is not denying access

我在玩拼图游戏。我有一个简单的可重现代码。

package university.harvard;

public class Pilot{
    public static void main(final String args[]){
        callInNotAExportedPackage();
    }
    private static void callInNotAExportedPackage(){
        try{
            final Class<?>classy = Class.forName("javax.swing.JButton");
            System.out.println(classy.newInstance());
        }catch(final Exception e){
            e.printStackTrace();
        }
    }           
}

我有这样的模块-info.java。

module John{
    exports university.harvard;
}

我可以通过这个命令编译模块。

C:\Ocp11>javac -d out --module-source-path src -m John

Note: src\John\Pilot.java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation for details.

我收到有关弃用的消息,但编译成功。在这一点上我说编译器不知道我会尝试调用一个 class in not a exported package.

当我运行模块

C:\Ocp11>java -p out -m John/university.harvard.Pilot

我可以看到正在通过反射检索实例,没有问题。

javax.swing.JButton[,0,0,0x0,invalid,alignmentX=0.0,alignmentY=0.5,border=javax.
swing.plaf.BorderUIResource$CompoundBorderUIResource@3498ed,flags=296,maximumSiz
e=,minimumSize=,preferredSize=,defaultIcon=,disabledIcon=,disabledSelectedIcon=,
margin=javax.swing.plaf.InsetsUIResource[top=2,left=14,bottom=2,right=14],paintB
order=true,paintFocus=true,pressedIcon=,rolloverEnabled=true,rolloverIcon=,rollo
verSelectedIcon=,selectedIcon=,text=,defaultCapable=true]

但是这是什么?我以为 Jigsaw 会屏蔽我

如果我像这样在代码中输入完全限定的 class 名称。

final Class<?>classy = Class.forName("javax.swing.JButton");
final javax.swing.JButton button = (javax.swing.JButton)classy.newInstance();
System.out.println(button);         

这次 Jigsaw 反应正确。

C:\Ocp11>javac -d out --module-source-path src -m John
src\John\Pilot.java:10: error: package javax.swing is not visible
            final javax.swing.JButton button = (javax.swing.JButton)classy.newIn
stance();
                       ^
  (package javax.swing is declared in module java.desktop, but module John does
not read it)
src\John\Pilot.java:10: error: package javax.swing is not visible
            final javax.swing.JButton button = (javax.swing.JButton)classy.newIn
stance();
                                                     ^
  (package javax.swing is declared in module java.desktop, but module John does
not read it)
Note: src\John\Pilot.java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
2 errors

但是我没有输入完全限定的 class 名称我可以绕过 Jigsaw。

我知道如果不参考它我无能为力,但我认为 Jigsaw 允许我这样做有些奇怪。

我正在使用

C:\Ocp11>java --version
java 14 2020-03-17
Java(TM) SE Runtime Environment (build 14+36-1461)
Java HotSpot(TM) 64-Bit Server VM (build 14+36-1461, mixed mode, sharing)

  

问题的标题是

Java 14 Jigsaw reflection to a class in not a Exported package is not denying access

但实际上,您尝试访问的 class 在导出包中。 class javax.swing.JButton 在模块 java.desktop 的包 javax.swing 中并且该包被导出:

// [License & Javadoc]
module java.desktop {
    // [other exports and requires]
    exports javax.swing;
    // [other exports, opens, uses and provides]
}

由于模块在 运行 时间可用,Class.forName("javax.swing.JButton") 不会 抛出异常(否则它会抛出 java.lang.ClassNotFoundException) .它可能可用,因为它是一个根模块(参见 here and here)。

即使 class 不在导出包中,这也行得通:

Class<?> aClass = Class.forName("sun.java2d.marlin.Curve"); // will compile and run

但是,(javax.swing.JButton)classy.newInstance(); 不会编译 — 不是因为包没有导出,而是因为您的模块 John 没有读取它。为此,您的模块将 requires java.desktop; 像这样:

module John {
    requires java.desktop;

    exports university.harvard;
}

这样模块 John 将能够读取 java.desktop 的所有导出包。

调用 classy.newInstance();(没有类型转换)将编译,因为编译器不知道新实例的类型。

以下是一些在 运行 时和编译时有效的示例,以及为什么(不):

// (1) will compile and run:
Class<?> curveClass = Class.forName("sun.java2d.marlin.Curve");

// (2) will compile but not run, even if the module `requires java.desktop;`:
Object curveObject = curveClass.newInstance();

// (3) will not compile and not run, even if the module has `requires java.desktop;`:
sun.java2d.marlin.Curve curve = (sun.java2d.marlin.Curve) curveClass.newInstance();

// (4) will compile and run:
Class<?> jButtonClass = Class.forName("javax.swing.JButton");

// (5) will compile and run, even if the module does not have `requires java.desktop;`:
Object jButtonObject = jButtonClass.newInstance();

// (6) will only compile if the module `requires java.desktop;`:
javax.swing.JButton jButton = (javax.swing.JButton) jButtonClass.newInstance();
  1. 仅保留 class 的信息/特征,仅此而已,因此这种访问是可以的。
  2. 将进行编译,因为编译器不知道新实例的类型为 sun.java2d.marlin.Curve。但是,在 运行 时,访问将被阻止。
  3. 甚至不会编译,因为编译器知道类型,因此知道访问是非法的。
  4. 同(1)
  5. 将编译,原因与 (2) 相同,将 运行 因为包实际上已导出
  6. 仅当模块具有 requires java.desktop; 时才会编译,因为编译器知道类型