如果父类/接口发生变化,类的字节码是否会发生变化?

时间:2013-02-26 18:53:24

标签: java class bytecode

我正在尝试确定是否需要在构建链中重新编译一些jar,如果我有例如以下结构,jar 1在其'source更改时编译,jar 2在其'source更改时或jar时编译1已重新编译。

jar 1:

public class Foo /* impl*/

jar 2:

public class Bar extends Foo /*impl*/

假设两个班级之间的合同没有改变,即。添加抽象方法或将方法添加到接口等

我需要重新编译jar 2吗?即。如果对Foo中的私有方法进行了一些更改,Bar需要重新编译吗?

我尝试通过在一组中更改一堆之后比较两个类的字节码来测试它,并且正如预期的那样它没有改变。然而,我的同事坚持认为他们遇到过这样的情况:即使合同没有改变,他们也必须重新编译一切才能工作,但是他们不记得原因是什么......所以举证责任在我身上证明了这不应该是必要的。 是否存在更改超类需要重新编译子类的情况,即使两者之间的接口保持不变也是如此?

3 个答案:

答案 0 :(得分:6)

假设Foo由开源组织发布;各种公司实施了数以千计的Foo子类。

现在,如果对Foo进行了一些更改,并且以二进制形式发布了新版本,那么所有公司都应该重新编译它们的代码吗?当然不是。 (好吧,我们一直在重新编译所有代码,但这不是必需的 - 可以简单地放入Foo的新jar而不会导致任何问题)

这是二进制兼容性问题,您可以查看规范以确保对Foo的更改是安全的。见http://docs.oracle.com/javase/specs/jls/se7/html/jls-13.html

答案 1 :(得分:3)

一般情况下,您必须重新编译依赖类。但是,如果您未更改Foo使用的Bar的任何方法或字段,则在更改Bar时无需重新编译Foo

例如,Foo protected int foo()调用方法Bar,但您将其签名更改为protected String foo()或将其可见性更改为private,则必须重新设置Bar - 编译Bar。在这种情况下,Bar无法编译:您必须更改其代码。

但是,如果foo()未使用方法foo()或仅更改了Bar的实施细节,则可以使用{{1}}而无需重新编译。

答案 2 :(得分:2)

绝对不会。您可以使用打包在jar文件中的框架(包含许多类似于Foo的合同的类),而不需要从源代码编译它们。但是你需要绝对确保合同没有直接间接改变。间接变化的一些例外:

public class Foo { //v1
    public static final int CONSTANT = 1;         
}

public class Foo { //v2
    public static final int CONSTANT = 2;         
}

public class Bar extends Foo {
    private int a(int value) {
        switch (value) {
            case CONSTANT:
                return 1;
        }
        return 2;
    }
}

如果不重新编译类Bar,它仍将使用值1.