Poison

Java 为什么对常量的修改没有生效?

写这篇文章的起因是前几天同事改了一个常量类中的提示,发布到测试环境后没有生效,正好看 《Java 解惑(谜题 93: 类的战争)》 提到了这个问题,所以写篇文章记录一下。

以下均使用命令行进行演示,至于为什么没有使用 IDE 后面会提到。

先看一个简单的 Constants 类:

1
2
3
4
5
6
7
8
/**
* Created by Poison on 15/05/2017.
*/
public class Constants {
public static final String a = "before fixing";
}

再看下 Solution 类:

1
2
3
4
5
6
7
8
9
10
/**
* Created by Poison on 15/05/2017.
*/
public class Solution {
public static void main(String[] args) {
System.out.println(Constants.a);
}
}

编译,运行 Solution 的主函数,毫无疑问结果如图:

before fixing

现在我们把 Constants 类修改为:

1
2
3
4
5
6
7
8
/**
* Created by Poison on 15/05/2017.
*/
public class Constants {
public static final String a = "after fixing";
}

重新编译 Constants 类,再运行 Solution 的主函数,输出结果如图:

after fixing

为什么修改没有生效?是 Constants 类的问题,还是 Solution 类的问题?

我们先反编译 Constants 类看看:

javap Constants

由上图可见,对 Constants 类的修改是生效的。

再看反编译的 Solution 类:

javap Solution

看到这里,原因也就明确了,常量变量会被编译进那些引用它们的类中。这和笔者同事前几日遇到的情况一模一样,同事在本地开发时修改了常量类中的常量字段的值,本地是生效的,原因是因为本地开发使用了 IDE,而 IDE 将引用到常量类的类也重新编译了,所以能看到最新的值,而在发布到测试环境的过程中,打包机仅仅将常量类所属的模块进行了重新编译,未将引用常量的类的模块重新编译,所以当时看见的是更改前的值,同事将常量类的 class 文件反编译后看见的也是修改后的值,但是却忘了看引用该常量类的类,所以当时没有发现这个问题。

Java 解惑
Java 虚拟机规范(Java SE 8版)