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

Kun*_*nal 79 java runtime classpath compile-time

Java中的编译时间和运行时依赖性有什么区别?它与类路径有关,但它们有何不同?

gpe*_*che 70

  • 编译时依赖:您需要依赖于CLASSPATH编译工件.生成它们是因为您对代码中的硬编码具有某种"引用",例如调用new某个类,扩展或实现某些内容(直接或间接),或者使用直接reference.method()表示法调用方法.

  • 运行时依赖性:您需要依赖于CLASSPATH运行工件.生成它们是因为您执行访问依赖项的代码(以硬编码方式或通过反射或其他方式).

尽管编译时依赖性通常意味着运行时依赖性,但您可以只具有编译时依赖性.这是基于Java仅在首次访问该类时链接类依赖性这一事实,因此如果您从未在运行时访问特定类,因为永远不会遍历代码路径,Java将忽略该类及其依赖项.

这样的例子

在C.java中(生成C.class):

package dependencies;
public class C { }
Run Code Online (Sandbox Code Playgroud)

在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());
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,A对一个编译时依赖C通过B,但只会对下的运行时间依赖性,如果你通过执行时一些参数java dependencies.A,因为JVM将只尝试解决B上的依赖C时,它得到执行B b = new B().此功能允许您在运行时仅提供在代码路径中使用的类的依赖项,并忽略工件中其余类的依赖项.

  • 我知道这现在是一个非常古老的答案,但是 JVM 如何从一开始就没有 C 作为运行时依赖项?如果它能够识别出“这是对 C 的引用,是时候将其添加为依赖项了”,那么 C 是否已经本质上是一个依赖项,因为 JVM 可以识别它并知道它在哪里? (2认同)

Mar*_*ten 31

一个简单的例子就是看一下像servlet api这样的api.要使servlet编译,需要servlet-api.jar,但在运行时servlet容器提供了一个servlet api实现,因此您不需要将servlet-api.jar添加到运行时类路径中.

  • 'provided'表示包含在编译时,但不要将它捆绑在WAR或其他依赖项集合中.'runtime'执行相反的操作(在编译时不可用,但与WAR打包在一起). (2认同)

Jas*_*n S 27

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

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的对象,并且该类仅在一个特定场景中实例化,那么除了在那个场景.

  • 不可以。您需要阅读 Java 中编译和链接的工作原理。当编译器引用外部类时,它所关心的是如何“使用”该类,例如它的方法和字段是什么。它不关心外部类的方法中实际发生的情况。如果 L1 调用 L2,那就是 L1 的实现细节,并且 L1 已经在其他地方编译过。 (2认同)

JOT*_*OTN 11

Java在编译时实际上并没有链接任何内容.它仅使用它在CLASSPATH中找到的匹配类来验证语法.直到运行时才将所有内容组合在一起并在当时基于CLASSPATH执行.


Bal*_*usC 10

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