导入的java类中的public static final变量

时间:2009-11-07 13:53:18

标签: java static final public recompile

我碰巧在工作场所遇到过Java代码。方案如下:有两个类 - ClassAClassB

除了4个公共静态最终字符串值之外,

ClassA没有任何内容。它的目的是使用像ClassA.variable这样的值(不要问我为什么,这不是我的代码)。

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

这仅仅是因为JDK 1.6或者我之前应该知道重新编译ClassB!开导我。 :)

5 个答案:

答案 0 :(得分: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)使编译器能够注意到乘法没有区别,并且可以一起省略。

答案 1 :(得分:3)

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

答案 2 :(得分:2)

为什么要尝试单独编译类?

使用像maven或ant这样的构建系统,或者让IDE执行它。

唯一安全的做法是重新编译每个依赖java类的java,这个java已经改变,直到每个可以生成的类都被重新编译。

答案 3 :(得分:2)

如果你没有在开关中使用这些值,你可以改为:

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

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

然后编译器将不再对使用它们的其他类中的值进行硬编码。

答案 4 :(得分:2)

假设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)方法。