Java常数在不同的地方是不同的



所以,我们已经将某些内容推向了生产,并且它运行良好。然而,我在日志中看到了一些非常令人不安的东西。即:

pr 0.1 -  wait time for fetchAndRemoveEntries: 0
pr 1.0 - f'n'r entries: uid: hbyk68jfhbf5th

注意,pr对于这两行是不同的。运行代码搜索wait time for fetchAndRemoveEntries只返回一个结果,f'n'r entries也是如此。都是println。第一个在我们的一个API类中:

System.out.println("pr " + NotificationDataStorage.printReduction + " -  wait time for fetchAndRemoveEntries: " + (System.currentTimeMillis() - startTime));

第二个在我们的NotificationDataStorage类中找到,其中定义了printReduction。类的缩写版本:

public class NotificationDataStorage {
  public static final double printReduction = 1;
  ...
  public static void addEntries(ArrayList<HashMap> data) {
    ... // No, I have did not declare 'printReduction' as a local variable.)
    System.out.println("pr " + printReduction + " - adding " + data.size() + " entries");
    ...
  }
  ...
}

即使忽略"addEntries"代码,NotificationDataStorage。printReduction返回0.1,而实际上它应该是1,这是非常重要的。

我们实际上已经被这样的问题困扰了整整一周,尽管到目前为止,当我们推送到服务器时,它们总是消失。我们认为它们是本地的怪癖,也许是NetBeans的缺陷。我在调试器中逐步执行代码,查看传递给方法a的常量,并看到它在进入a的作用域后变为以前构建的值。旧值导致代码崩溃。我将它设置为记录一些东西,然后问题神秘地消失了。我们曾经有过代码莫名其妙地不能工作,但在签出另一个分支并签回不能工作的分支后,它突然又开始工作了。清洁和建造有时可以修复它。有时不是。

奇怪的是,据我所知,这是我们遇到这些问题的第一周。就好像一周前我们写了一些代码,这些代码现在召唤了一个恶魔来扰乱我们的常量。或者,更准确地说,就好像当我们更新常量时,变化只反映在使用它们的某些地方。

当前显示的特定问题没有问题,但非常令人担忧的是,其他常量可能会遇到类似的问题。

有人对此有什么见解或经验或解释吗?

这几乎肯定是由于不是所有的代码都被重新编译。

您的字段是public static final double,它将在编译时获得内联值。如果值发生变化,这将是一个巨大的问题,因为没有重新编译的旧类将不会更新它们的值!如果引用这个字段的代码在不同的jar中,它可能也不会被重新编译。

Java安全编码指南DCL59-J涵盖了这个问题

final关键字可用于指定常数值(即在程序执行期间不能更改的值)。但是,在程序生命周期中可能发生变化的常量不应该声明为public final。Java语言规范(JLS) [JLS 2013]允许实现在读取该字段的任何编译单元中插入任何公共final字段的值。因此,如果对声明类进行了编辑,使得新版本为该字段提供了不同的值,那么读取公共final字段的编译单元在重新编译之前仍然可以看到旧值。例如,当第三方库更新到最新版本但未重新编译引用代码时,可能会出现此问题。

解决方案是将字段设置为private,并使用公共静态getter方法。

 private static final double PRINT_REDUCTION = 1; //will change often
 public static double getPrintReduction(){
        return PRINT_REDUCTION;
 }

注意,我还将字段大写,这是java对常量的编码约定。

这个主题和更多的问题都在Java编码指南推荐书

最新更新