依赖放在 classpath 还是 module path 上,可见性会变:哪些包看得到、能不能反射、同名包怎么处理。这篇用同一个小库放在不同路径上逐项真实运行,包括自动模块的命名规则和 JDK 内部 API 的访问限制。
编译 Direct(直接引用 pkg.internal.Secret),liba 在 classpath 上 → 返回码 0 (classpath 上所有包都可见)
同样的源码,liba(有 module-info,只导出 pkg.api)在 module path 上 → 返回码 1 ; ['app2/Direct.java:1: error: package pkg.internal is not visible']
运行 Main,liba 在 classpath 上(未命名模块):
Api.hi() = api,Api 的模块 = unnamed module @6bc7c054,是否命名模块 = false
Class.forName("pkg.internal.Secret") 成功,所在模块 unnamed module @6bc7c054
反射调用公开类的公开构造器成功
setAccessible(true) 读私有字段成功:private-field
反射调用公开类的 public 静态方法 → internal-value
运行 Main,liba 在 module path 上(命名模块 liba,只导出 pkg.api):
Api.hi() = api,Api 的模块 = module liba,是否命名模块 = true
Class.forName("pkg.internal.Secret") 成功,所在模块 module liba
反射 newInstance → IllegalAccessException: class Main cannot access class pkg.internal.Secret (in module liba) because module liba does not export pkg.internal to unnamed module @433c675d
读私有字段 → InaccessibleObjectException: Unable to make field private java.lang.String pkg.internal.Secret.hidden accessible: module liba does not "opens pkg.internal" to unnamed module @433c675d
反射调用 public 静态方法 → IllegalAccessException: class Main cannot access class pkg.internal.Secret (in module liba) because module liba does not export pkg.internal to unnamed module @433c675d
再加 --add-opens liba/pkg.internal=ALL-UNNAMED:
setAccessible(true) 读私有字段成功:private-field
只加 --add-exports liba/pkg.internal=ALL-UNNAMED(导出但不 opens):
读私有字段 → InaccessibleObjectException: Unable to make field private java.lang.String pkg.internal.Secret.hidden accessible: module liba does not "opens pkg.internal" to unnamed module @5197848c jar --describe-module liba-1.2.jar: No module descriptor found. Derived automatic module. | | liba@1.2 automatic | requires java.base mandated | contains pkg.api | contains pkg.internal
java --module-path liba-1.2.jar --describe-module liba(自动模块名由文件名 liba-1.2.jar 推出:去掉版本号 -1.2): liba@1.2 file://<jtmp>/j134w/liba-1.2.jar automatic | requires java.base mandated | contains pkg.api | contains pkg.internal
运行 Main,liba-1.2.jar(没有 module-info)在 module path 上:
Api.hi() = api,Api 的模块 = module liba,是否命名模块 = true
Class.forName("pkg.internal.Secret") 成功,所在模块 module liba
反射调用公开类的公开构造器成功
setAccessible(true) 读私有字段成功:private-field
反射调用公开类的 public 静态方法 → internal-value
加了 Automatic-Module-Name: com.example.liba 之后: com.example.liba@1.2-am file://<jtmp>/j134w/liba-1.2-am.jar automatic | requires java.base mandated | contains pkg.api | contains pkg.internal
文件名 my-lib-3.0.1-SNAPSHOT.jar 推出的模块名: my.lib@3.0.1-SNAPSHOT file://<jtmp>/j134w/my-lib-3.0.1-SNAPSHOT.jar automatic | requires java.base mandated
文件名 123-lib.jar(以数字开头)→ Error occurred during initialization of boot layer | java.lang.module.FindException: Unable to derive module descriptor for 123-lib.jar classpath 顺序 v1.jar:v2.jar:
Util.v() = v1;Util 来自 v1.jar
Extra.e() = extra-only-in-v2
classpath 顺序 v2.jar:v1.jar:
Util.v() = v2;Util 来自 v2.jar
Extra.e() = extra-only-in-v2
两个 jar 放在 module path 上(都是自动模块 v1 与 v2,同一个包 pkg.same):返回码 1 ; Error occurred during initialization of boot layer | java.lang.module.ResolutionException: Module v2 contains package pkg.same, module v1 exports package pkg.same to v2
v1 在 module path 上、v2 在 classpath 上:
Util.v() = v1;Util 来自 v1.jar
Extra → NoClassDefFoundError [JDK 17,默认]
读 String.value → InaccessibleObjectException: Unable to make field private final byte[] java.lang.String.value accessible: module java.base does not "opens java.lang" to unnamed module @10742304
jdk.internal.misc.Unsafe → IllegalAccessException: class Priv cannot access class jdk.internal.misc.Unsafe (in module java.base) because module java.base does not export jdk.internal.misc to unnamed module @10742304
sun.misc.Unsafe.theUnsafe(jdk.unsupported 模块):成功,类型 sun.misc.Unsafe
[JDK 17,--add-opens java.base/java.lang=ALL-UNNAMED]
读 String.value:成功(byte[])
jdk.internal.misc.Unsafe → IllegalAccessException: class Priv cannot access class jdk.internal.misc.Unsafe (in module java.base) because module java.base does not export jdk.internal.misc to unnamed module @4d7e1886
sun.misc.Unsafe.theUnsafe(jdk.unsupported 模块):成功,类型 sun.misc.Unsafe
[JDK 17,--add-exports java.base/jdk.internal.misc=ALL-UNNAMED]
读 String.value → InaccessibleObjectException: Unable to make field private final byte[] java.lang.String.value accessible: module java.base does not "opens java.lang" to unnamed module @4d7e1886
jdk.internal.misc.Unsafe.getUnsafe() 调用:jdk.internal.misc.Unsafe
sun.misc.Unsafe.theUnsafe(jdk.unsupported 模块):成功,类型 sun.misc.Unsafe
[JDK 21,默认]
读 String.value → InaccessibleObjectException: Unable to make field private final byte[] java.lang.String.value accessible: module java.base does not "opens java.lang" to unnamed module @15db9742
jdk.internal.misc.Unsafe → IllegalAccessException: class Priv cannot access class jdk.internal.misc.Unsafe (in module java.base) because module java.base does not export jdk.internal.misc to unnamed module @15db9742
sun.misc.Unsafe.theUnsafe(jdk.unsupported 模块):成功,类型 sun.misc.Unsafe
[JDK 21,--add-opens java.base/java.lang=ALL-UNNAMED]
读 String.value:成功(byte[])
jdk.internal.misc.Unsafe → IllegalAccessException: class Priv cannot access class jdk.internal.misc.Unsafe (in module java.base) because module java.base does not export jdk.internal.misc to unnamed module @7adf9f5f
sun.misc.Unsafe.theUnsafe(jdk.unsupported 模块):成功,类型 sun.misc.Unsafe
[JDK 21,--add-exports java.base/jdk.internal.misc=ALL-UNNAMED]
读 String.value → InaccessibleObjectException: Unable to make field private final byte[] java.lang.String.value accessible: module java.base does not "opens java.lang" to unnamed module @7adf9f5f
jdk.internal.misc.Unsafe.getUnsafe() 调用:jdk.internal.misc.Unsafe
sun.misc.Unsafe.theUnsafe(jdk.unsupported 模块):成功,类型 sun.misc.Unsafe