You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java 9为何可运行Java 8编译的使用非导出包的代码?

问题解答:Unnamed Module为何能访问未导出的sun.util.calendar包

首先还原你的场景细节:
你用Java 8编译器编译了这段代码:

package pack;
import sun.util.calendar.CalendarUtils;
public class A {
    public static void main(String[] args) {
        System.out.println(CalendarUtils.isGregorianLeapYear(2018));
    }
}

编译时收到内部API的警告提示:

gyan@gyan-pc:~/codes/java$ ~/Documents/softwares/Linux/jdk1.8.0_131/bin/javac -d . a.java
a.java:2: warning: CalendarUtils is internal proprietary API and may be removed in a future release
import sun.util.calendar.CalendarUtils;
^
a.java:9: warning: CalendarUtils is internal proprietary API and may be removed in a future release
System.out.println(CalendarUtils.isGregorianLeapYear(2018));
^
2 warnings

你的默认JRE版本为Java 9:

gyan@gyan-pc:~/codes/java$ java -version
java version "9.0.1"
Java(TM) SE Runtime Environment (build 9.0.1+11)
Java HotSpot(TM) 64-Bit Server VM (build 9.0.1+11, mixed mode)

但使用Java 9运行编译后的代码却无报错,输出了false。你疑惑的点是:Unnamed Module依赖所有平台模块,但仅能使用对应模块导出的包,而java.base并未导出sun.util.calendar,为何还能访问?


这其实是Java 9模块系统为向后兼容特意保留的宽松访问规则,针对的就是Unnamed Module中的代码:

  1. Unnamed Module的特殊兼容权限
    你的代码因为没有module-info.java,运行时会被归入Unnamed Module。Java 9的模块系统对这个特殊模块开了“后门”:它允许Unnamed Module读取所有平台模块的所有包——包括那些未被导出的内部包。这是为了让大量依赖内部API的旧代码,能在Java 9及后续版本中先平稳运行,避免直接崩溃。

  2. 编译与运行时的规则差异
    Java 8本身没有模块系统,内部API可以自由访问,所以编译时仅会发出警告;到了Java 9运行时,模块系统虽然启用,但对Unnamed Module的代码放宽了访问检查。不过这种兼容是临时的,Java 11及以后的版本中,很多内部API被完全强封装或移除,这类代码再运行就会直接报错。

  3. 可验证的权限控制
    Java 9提供了--illegal-access参数来控制内部API的访问:默认值为permit,首次访问未导出API会发警告但允许执行;如果设置为deny,就会直接禁止访问。你可以试试这个命令,此时你的代码会抛出IllegalAccessError:

    java --illegal-access=deny pack.a
    

总结来说,这是Java模块系统过渡阶段的兼容妥协,但依赖内部API的代码并不推荐继续使用,后续版本会逐步收紧这类访问权限。

内容的提问来源于stack exchange,提问作者my name is GYAN

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:22:49