编译时间与运行时间依赖关系 – Java

Java中的编译时间和运行时间依赖关系有什么区别? 这与class级path有关,但它们又有什么不同呢?

  • 编译时依赖 :你需要CLASSPATH的依赖来编译你的工件。 它们的产生是因为你对代码中的硬编码依赖有某种“引用”,例如为某个类调用new ,直接或间接地扩展或实现某些东西,或者使用直接reference.method()方法调用方法reference.method()符号。

  • 运行时依赖关系 :您需要CLASSPATH的依赖项来运行您的工件。 它们是由于您执行访问依赖项的代码而产生的(以硬编码的方式或通过reflection或其他方式)。

虽然编译时间依赖通常意味着运行时间依赖,但是你可以只有编译时间依赖。 这是基于这样一个事实,即Java只在首次访问该类时链接类依赖关系,所以如果在运行时从不访问特定类,因为从不遍历代码path,那么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()); } } } 

在这种情况下, ACB具有编译时间依赖性,但是如果在执行java dependencies.A时传递一些参数,它将只对C具有运行时依赖性A ,因为JVM只会尝试解决B '当它执行B b = new B()时,它依赖于C 此function允许您在运行时仅提供您在代码path中使用的类的依赖关系,并忽略工件中其余类的依赖关系。

一个简单的例子就是像servlet api那样查看api。 为了使你的servlet能够编译,你需要servlet-api.jar,但是在运行时servlet容器提供了一个servlet api实现,所以你不需要把servlet-api.jar添加到你的运行时类path中。

编译器需要正确的类path才能编译对库的调用(编译时依赖性)

JVM需要正确的类path才能加载正在调用的库中的类(运行时相关性)。

他们可能在几个方面有所不同:

1)如果你的类C1调用了库类L1,而L1调用了库类L2,那么C1对L1和L2具有运行时依赖,但是对L1只有编译时依赖。

2)如果你的类C1使用Class.forName()或其他机制dynamic实例化一个接口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。

基本上,为了做任何有趣的事情,你的类需要在类path中与其他类和接口进行交互。 由该组库接口组成的类/接口图产生编译时间依赖关系链。 实现产生运行时依赖链。 请注意,运行时依赖链是运行时依赖的或失败缓慢的:如果L1的实现有时依赖于实例化一个类L2的对象,并且该类只在一个特定的场景中被实例化,那么除了在那个场景。

Java在编译时并不实际链接任何东西。 它只使用它在CLASSPATH中find的匹配类来validation语法。 直到运行时,所有的东西都放到一起,并且在那个时候基于CLASSPATH执行。

Compiletime依赖关系只是您在编译的类中直接使用的依赖关系(其他类)。 运行时依赖关系覆盖了正在运行的类的直接和间接依赖关系。 因此,运行时依赖关系包括依赖关系的依赖关系和任何reflection依赖关系,比如您在String中使用但在Class#forName()中使用的Class#forName()

对于Java,编译时间依赖性是您的源代码的依赖关系。 例如,如果A类从B类调用一个方法,那么A在编译时依赖于B,因为A必须知道要编译的B(Btypes)。 这里的技巧应该是这样的:编译的代码还不是一个完整的,可执行的代码。 它包括可replace的地址(符号,元数据),这些地址尚未编译或存在于外部jar子中。 在链接期间,这些地址必须由内存中的实际地址replace。 要做到这一点,正确的符号/地址应该创build。 这可以用类(B)的types来完成。 我相信这是编译时的主要依赖。

运行时依赖关系与实际的控制stream程更相关。 它调用实​​际的内存地址。 这是程序运行时的依赖关系。 这里需要类B的细节,而不仅仅是types信息。 如果类不存在,那么你将得到RuntimeException和JVM将退出。

这两个依赖关系一般也不应该stream向相同的方向。 这是OOdevise的一个问题。

在C ++中,编译有点不同(不是及时的),但它也有一个链接器。 所以这个过程可能会被认为与Java相似。