首页 文章

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

提问于
浏览
73

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

6 回答

  • 67
    • Compile-time dependency :您需要 CLASSPATH 中的依赖项来编译您的工件 . 它们之所以产生,是因为你在代码中有一些硬编码的依赖关系,例如为某些类调用 new ,扩展或实现某些东西(直接或间接),或者使用直接 reference.method() 符号进行方法调用 .

    • Run-time dependency :您需要 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());
            }
        }
    }
    

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

  • 1

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

  • 10

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

    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有编译时依赖性 .

    Basically, to do anything interesting, your class needs to interface with other classes and interfaces in the classpath. The class/interface graph formed by that set of library interfaces yields the compile-time dependency chain. The library implementations yield the run-time dependency chain. 请注意,运行时依赖关系链是运行时依赖或失败 - 缓慢:如果L1的实现有时依赖于实例化类L2的对象,并且该类仅在一个特定场景中实例化,那么除了在那种情况下 .

  • 24

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

  • 11

    Compiletime依赖项只是您在运行的类中直接使用的依赖项(其他类) . 因此,运行时依赖项包括依赖项的依赖项和任何反射依赖项,如 String 中的类名,但在 Class#forName() 中使用 .

  • 29

    对于Java,编译时依赖性是源代码的依赖性 . 例如,如果类A从类B调用方法,则A在编译时依赖于B,因为A必须知道要编译的B(B的类型) . 这里的诀窍应该是:编译代码还不是完整的可执行代码 . 它包括尚未编译或存在于外部jar中的源的可替换地址(符号,元数据) . 在链接期间,这些地址必须由内存中的实际地址替换 . 要正确执行,应创建正确的符号/地址 . 这可以通过类(B)的类型来完成 . 我相信这是编译时的主要依赖 .

    运行时依赖性与实际控制流更相关 . 它会侵入实际的内存地址 . 这是程序运行时的依赖关系 . 您需要B类详细信息,例如实现,而不仅仅是类型信息 . 如果该类不存在,那么您将获得RuntimeException并且JVM将退出 .

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

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

相关问题