Java中的编译时间和运行时依赖性之间有什么区别?它与类路径有关,但是它们有何不同?
Answers:
编译时依赖性:需要依赖项CLASSPATH
来编译工件。之所以产生它们,是因为您对代码中的硬编码有某种“引用”,例如调用new
某个类,扩展或实现某些内容(直接或间接)或使用直接reference.method()
符号的方法调用。
运行时依赖项:CLASSPATH
运行工件时需要依赖项。之所以产生它们,是因为您执行访问该依赖项的代码(以硬编码方式或通过反射或其他方式)。
尽管编译时依赖关系通常意味着运行时依赖关系,但是您可以具有仅编译时依赖关系。这基于这样一个事实,即Java仅在第一次访问该类时才链接类依赖关系,因此,如果由于从未遍历代码路径而从未在运行时访问特定类,则Java将忽略该类及其依赖关系。
这个例子
在C.java中(生成C.class):
package dependencies;
public class C { }
在A.java中(生成A.class):
package dependencies;
public class A {
public static class B {
public String toString() {
C c = new C();
return c.toString();
}
}
public static void main(String[] args) {
if (args.length > 0) {
B b = new B();
System.out.println(b.toString());
}
}
}
在这种情况下,A
对一个编译时依赖C
通过B
,但只会对下的运行时间依赖性,如果你通过执行时一些参数java dependencies.A
,因为JVM将只尝试解决B
的依赖上C
时,它得到执行B b = new B()
。此功能允许您在运行时仅提供在代码路径中使用的类的依赖关系,而忽略工件中其余类的依赖关系。
一个简单的示例是查看类似于servlet api的api。要使servlet编译,您需要servlet-api.jar,但是在运行时servlet容器提供了servlet api实现,因此您不需要将servlet-api.jar添加到运行时类路径中。
编译器需要正确的类路径才能编译对库的调用(编译时间相关性)
JVM需要正确的类路径,以便将类加载到正在调用的库中(运行时依赖项)。
它们可能在几个方面有所不同:
1)如果您的类C1调用库类L1,而L1调用库类L2,则C1对L1和L2具有运行时依赖性,而对L1仅具有编译时依赖性。
2)如果您的类C1使用Class.forName()或其他某种机制动态实例化接口I1,并且接口I1的实现类是类L1,则C1对I1和L1具有运行时依赖关系,但只有编译时依赖关系在I1上。
在编译时和运行时相同的其他“间接”依赖项:
3)您的类C1扩展了库类L1,而L1实现了接口I1并扩展了库类L2:C1对L1,L2和I1具有编译时依赖性。
4)您的类C1有一个方法foo(I1 i1)
和一个方法bar(L1 l1)
,其中I1是接口,而L1是带有参数的类,该参数是接口I1:C1对I1和L1具有编译时依赖性。
基本上,要做任何有趣的事情,您的类都需要与其他类和类路径中的接口进行接口。由那组库接口形成的类/接口图产生了编译时依赖链。库的实现产生了运行时依赖链。请注意,运行时依赖关系链是运行时依赖的或失败缓慢的:如果L1的实现有时依赖于实例化类L2的对象,并且该类仅在一种特定的情况下实例化,则除了在这种情况。
编译时依赖项仅是您直接在要编译的类中使用的依赖项(其他类)。运行时依赖关系涵盖了正在运行的类的直接和间接依赖关系。因此,运行时依赖项包括依赖项的依赖项以及任何反射依赖项,例如您在中具有String
但在中使用的类名Class#forName()
。
A
,B.jar与B extends A
C.jar一起使用,C extends B
则C.jar取决于A.jar的编译时间,即使C对A的依赖是间接的。
对于Java,编译时依赖性是源代码的依赖性。例如,如果类A从类B调用方法,则在编译时A依赖于B,因为A必须知道要编译的B(B的类型)。这里的窍门应该是:编译代码还不是完整的可执行代码。它包括尚未编译或在外部jar中存在的源的可替换地址(符号,元数据)。链接期间,这些地址必须用内存中的实际地址替换。要正确执行此操作,应创建正确的符号/地址。这可以通过类(B)的类型来完成。我相信这是编译时的主要依赖项。
运行时依赖性与实际控制流关系更大。它涉及实际的内存地址。它是程序运行时的依赖项。您在这里需要B类的详细信息,例如实现,而不仅仅是类型信息。如果该类不存在,则将获得RuntimeException且JVM将退出。
两种依赖关系(通常也不应)遵循相同的方向。不过,这是面向对象设计的问题。
在C ++中,编译有些不同(不是实时的),但是它也有一个链接器。因此,我认为该过程可能与Java类似。