2010-11-24 151 views

回答

18

编译器需要以编译成库调用合适的类路径(编译时依赖)

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的对象,并且该类只在一个特定的场景中被实例化,那么就没有依赖关系在这种情况下。

+0

实例1中的编译时间依赖不应该是L1吗? – BalusC 2010-11-24 20:06:30

+0

是,修正了一些错别字。 – 2010-11-24 20:06:49

+0

谢谢,但在运行时如何加载类的工作?在编译时很容易理解。但是在运行时,它是如何操作的,当我有两个不同版本的Jars时?它会选择哪一个? – Kunal 2010-11-24 20:50:59

10

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

9

编译时间依赖关系只是您正在编译的类中使用的直接的依赖关系(其他类)。运行时依赖包括您正在运行的类的直接和间接依赖关系。因此,运行时依赖关系包括依赖关系的依赖关系以及您在String中使用但在Class#forName()中使用的类名称之类的任何反射依赖关系。

22

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

50
  • 编译时依赖:您需要在您的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编译时依赖于CB,但如果在执行时传递一些参数,它将只对C具有运行时依赖性,因为JVM在执行B b = new B()时只会尝试解决BC的依赖。此功能允许您在运行时仅提供您在代码路径中使用的类的依赖关系,并忽略工件中其余类的依赖关系。

1

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

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

这两个依赖关系,通常和不应该流向相同的方向。不过这是OO设计的问题。

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