【问题标题】:public static final variable in an imported java class导入的 java 类中的公共静态最终变量
【发布时间】:2010-12-14 03:59:32
【问题描述】:

我在工作场所碰巧遇到了 Java 代码。场景如下:有 2 个类 - ClassAClassB

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

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

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

【问题讨论】:

    标签: java static final public recompile


    【解决方案1】:

    如果类ClassA 中的final 变量的值恰好是编译时常量,编译器可能已使用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)使编译器能够注意到乘法没有区别,并且可以一起省略。

    【讨论】:

    • 那么,你是说 public static final 是编译时间常数吗?不知道。以为它只是一个常数,不能在运行时修改!感谢您的帮助。
    • 好答案。如果您想查看变量是否被内联,您可以使用 javap 查看类是如何编译的,例如“javap -c 标志”。
    【解决方案2】:

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

    【讨论】:

      【解决方案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) 方法。

      【讨论】:

        【解决方案4】:

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

        使用 maven 或 ant 之类的构建系统,或者让您的 IDE 来做。

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

        【讨论】:

        • 其中一个或其他类可能不在您的控制之下。
        【解决方案5】:

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

        public class A
        {
            public static final int FOO;
            public static final String BAR;
        
            static
            {
                FOO = 42;
                BAR = "Hello, World!";
            }
        }
        

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-08-28
          • 1970-01-01
          • 1970-01-01
          • 2015-04-07
          • 2012-04-20
          相关资源
          最近更新 更多