Maven Java:编译器和JRE都需要访问所有第三方类文件吗? 我有15年的C++经验,但对java来说是新的。我试图理解Java是如何处理缺少头文件的问题的。我有几个关于这个问题的问题

Maven Java:编译器和JRE都需要访问所有第三方类文件吗? 我有15年的C++经验,但对java来说是新的。我试图理解Java是如何处理缺少头文件的问题的。我有几个关于这个问题的问题,maven,javac,java,Maven,Javac,Java,具体来说,假设我为类“a”编写源代码,该类导入第三方类“Z”(并使用Z)。我知道在编译时,Java编译器必须能够“访问”有关Z的信息,以便编译A.Java,创建A.class。因此,Z.java或Z.class(或包含其中一个的JAR;比如Z.JAR)必须在编译时出现在本地文件系统上-正确吗 编译器是否使用类加载器来加载Z(在编译时重申) 如果在编译时使用类加载器是正确的,那么如果需要用户定义的类加载器(L),并且它是正在编译的项目的一部分,该怎么办?例如,假设L负责在运行时通过网络下载Z.cl

具体来说,假设我为类“a”编写源代码,该类导入第三方类“Z”(并使用Z)。我知道在编译时,Java编译器必须能够“访问”有关Z的信息,以便编译A.Java,创建A.class。因此,Z.java或Z.class(或包含其中一个的JAR;比如Z.JAR)必须在编译时出现在本地文件系统上-正确吗

编译器是否使用类加载器来加载Z(在编译时重申)

如果在编译时使用类加载器是正确的,那么如果需要用户定义的类加载器(L),并且它是正在编译的项目的一部分,该怎么办?例如,假设L负责在运行时通过网络下载Z.class?在这个场景中,Java编译器如何在编译时获得Z.class?它会先尝试编译L,然后在编译时使用L来获得Z吗

我知道,使用Maven构建项目时,Z.jar可以在编译时位于internet上的远程存储库中—可以位于ibiblio上,也可以位于POM文件中定义的自定义存储库中。我希望我是正确的,是MAVEN负责在编译时下载第三方JAR文件,而不是编译器的JVM

但是,请注意,在运行时,A.class再次需要Z.class—JRE如何知道从何处下载Z.class(没有Maven的帮助)?或者开发人员有责任将Z.class与A.class一起提供给应用程序(比如在JAR文件中)?(…假设未使用用户定义的类加载器。)

现在是一个相关的问题,仅供确认:我假设编译后,a.class只包含到Z.class的符号链接——Z.class的字节码不是a.class的一部分;如果我错了,请纠正我。(在C++中,静态链接将从Z.class into A.class复制字节,而动态链接则不会)。 关于编译过程的另一个相关问题:一旦描述Z的必要文件在编译时位于类路径上,编译器是否需要Z.class中的字节码来编译A.java(并将在必要时从Z.java构建Z.class),或者Z.java是否足以编译器


我的总体困惑可以总结如下。似乎Z的完整[字节]代码需要出现两次—一次在编译期间,第二次在运行时—并且对于Java程序引用的所有类都必须如此。换句话说,每个类必须下载/呈现两次。在编译期间,没有一个类可以仅仅表示为头文件(就像在C++中一样)。

您的摘要是正确的,但是我想补充一点,如果您编译到jar,那么jar将包含Z(如果Z是jar,则只包含Z jar中需要的文件)

但是,相同的Z可以用于编译和运行时

编译器是否使用类加载器来加载Z(在编译时重申)

几乎可以。它使用了一个类加载器,在许多方面都像类加载器一样。但实际上它并不加载类,因为它需要从
.java
文件以及
.class
文件创建类签名

我希望我是正确的,是MAVEN负责在编译时下载第三方JAR文件,而不是编译器的JVM

是的,Maven可以关闭JAR,尽管可以实现一个行为类似于
URLClassLoader
的JavaFileManager。Maven管理JAR的本地缓存,并根据需要从网络中填充该缓存

关于编译过程的另一个相关问题:一旦描述Z的必要文件在编译时位于类路径上,编译器是否需要Z.class中的字节码来编译A.java(并将在必要时从Z.java构建Z.class),或者Z.java是否足以编译器

它不需要所有字节码,只需要类、方法、属性签名和元数据。 如果A依赖于Z,那么可以通过源路径上的Z.java、任何(类路径、系统类路径)上的Z.class或通过一些自定义扩展(如Z.jsp)来满足该依赖性

我的总体困惑可以总结如下。似乎完整的[字节]Z的代码需要出现两次——一次在编译期间,第二次在运行时——并且对于Java程序引用的所有类都必须如此。换句话说,每个类都必须下载/出现两次。在编译期间,没有一个类可以仅表示为头文件(就像在C++中一样)

也许一个例子可以帮助澄清这一点。java语言规范要求编译器进行某些优化。
static final
primatives和
String
s的内联

如果类
A
仅依赖于
B
的常数:

class B {
  public static final String FOO = "foo";
}

class A {
  A() { System.out.println(B.FOO); }
}
然后可以编译、加载和实例化A,而不必在类路径上使用
B.class
。 如果您更改并使用不同的
FOO
值发布了一个
B.class
,那么a仍然具有编译时依赖性

因此,有可能存在编译时依赖项,而不是链接时依赖项

当然,通过反射,在没有编译时依赖项的情况下,有运行时依赖项是可能的

总之,在编译时,编译器确保类访问的方法和属性可用

在类加载时(运行时),字节码验证器检查预期的方法和属性是否确实存在。因此,字节码验证器双重检查假设