18

我碰巧在我的工作场所遇到了 Java 代码。这是场景:有 2 个类 -ClassAClassB.

ClassA除了里面的 4 个公共静态最终字符串值外,什么都没有。它的目的是使用这些值ClassA.variable(不要问我为什么,这不是我的代码)。

ClassB进口ClassA。我编辑了字符串值ClassA并编译了它。当我运行时,ClassB我可以看到它使用的是旧值 - 而不是新值。我必须重新编译ClassB以使其使用来自ClassA! (我不得不重新编译其他导入的类ClassA!)

这仅仅是因为 JDK 1.6 还是我早该知道要重新编译ClassB!开导我。:)

4

5 回答 5

23

如果final类中的变量值ClassA恰好是编译时常量,编译器可能会将它们内联到类中,ClassA而不是生成运行时引用。我认为,这就是您描述的案例中发生的情况。

例子:

public class Flags {
    public static final int FOO = 1;
    public static final int BAR = 2;
}

public class Consumer {
    public static void main(String[] args) {
         System.out.println(Flags.FOO);
    }
}

在此示例中,编译器可能会将 的值FOO合并到生成的代码中,Consumer而不是生成等效的运行时引用。如果稍后更改该值FOO,则必须重新编译Consumer才能使其使用新值。

这是一种优化,在编译程序的效率和速度方面具有一些优势。例如,内联值可能会进一步优化使用它的表达式,例如:

int x = Flags.FOO * 10;

在此示例中,内联值(此处为:1)使编译器能够注意到乘法没有区别,并且可以一起省略。

于 2009-11-07T14:10:49.627 回答
3

这是一个二进制兼容性问题。对常量字段的引用在编译时解析。您看到的行为是正确的;如果更改 A 类中的值,则必须重新编译客户端(B 类)。为避免此类问题,请考虑使用 Java 5.0 版中引入的枚举类型添加常量。

于 2009-11-07T14:19:48.490 回答
3

假设 ClassA 看起来像这样:

public class ClassA {
    public static final int FOO = 1;
    public static final int BAR = 2;
}

如果您重新编译它,ClassB 将继续使用旧值。我想这可能取决于编译器,但我认为这是典型的行为。如果您不想每次 ClassA 中的常量更改时都重新编译 ClassB,则必须执行以下操作:

public class ClassA {
    public static final int FOO = CONST(1);
    public static final int BAR = CONST(2);

    public static int CONST(int i) { return i; }
}

因为现在 javac 不愿意内联常量。相反,它会在 ClassA 的静态初始化程序运行时调用 CONST(int) 方法。

于 2012-08-22T01:45:10.533 回答
2

你为什么要单独编译这些类?

使用像 maven 或 ant 这样的构建系统,或者让你的 IDE 来做。

唯一安全的做法是重新编译依赖于已更改的 java 类的每个 java,直到重新编译每个可能受影响的类。

于 2009-11-07T15:37:35.243 回答
2

如果您不使用开关中的值,则可以这样做:

public class A
{
    public static final int FOO;
    public static final String BAR;

    static
    {
        FOO = 42;
        BAR = "Hello, World!";
    }
}

那么编译器将不再对正在使用它们的其他类中的值进行硬编码。

于 2009-11-07T20:26:05.627 回答