众所周知,Java会自动将字符串字面量(如"hello")放到常量池中,但作为整型字面量是否会被放到常量池中呢?下面进行实验
实验环境为:Java 1.8.0_212
实验一
public class Test{
public static void main(String[] args){
// 整型最大值
int i1 = 0x80000000;
int i2 = 1;
}
}
编译源码得到Test.class后,通过javap -verbose Test.class查看常量池信息:
从图中红色方框中,可以看到只有 0x80000000 被放入到了常量池,而 1 没有被放入到常量池。同样是以字面量写入到源代码中的,为什么会出现这种不一致现象呢?
猜想一下
0x80000000 与 1 的唯一区别是值的大小不同,会不会是Java编译器在编译时,以某个值作为阀值,将大于该值的整型字面量放入到常量池中,而小于该阀值的整型字面量就不放入到常量池中呢?下面用实验进行验证
实验二
public static void main(String[] args) {
// 单字节(byte)最大值
int i1 = 0b0111_1110;
// 单字节最小值
int i2 = -0b1000_0000;
// 双字节(char、short)最大值
int i3 = 0b0111_1111_1111_1111;
// 双字节最小值
int i4 = -32768;
// 四字节(int)最大值
int i5 = 0x7fff_ffff;
// 四字节最小值
int i6 = 0x8000_0000;
}
从图中红色方框中,可以看到只有字面量 0x7fffffff 和 0x8000_0000 被放入到了常量池。再结合代码逻辑可以推断出,区间(0b0111_1111_1111_1111,0x7fffffff]、[0x7fffffff,-32768)应该可以被放到常量池中,换句话说只要一个整型字面量要用2个字节以上进行存储,就会被放到常量池。
实验三
public class Test {
public static void main(String[] args) {
// Short.MAX_VALUE + 1
int i3 = 0b1000_0000_0000_0000;
// Short.MIN_VALUE - 1
int i4 = -32769;
}
}
编译源码得到Test.class后,通过javap -verbose Test.class查看常量池信息:
可以看到猜想得到了验证。
探究本质原因
为什么只有两个字节以上的整型变量才会被放入到常量池中呢?因为Java字节码指令只提供了
- iconst_n(1 <= n <=5)将1到5的整数放入到Java虚拟机栈中的操作数栈中;
- bipush PARAM 将-128到127的整数(一字节数)放入到Java虚拟机栈中的操作数栈中;
- sipush PARAM 将 -32769到32768的整数(两字节数)放入到Java虚拟机栈中的操作数栈中;
而对于超过三字节数进行表示的数无法通过指令的方式将其放到操作数栈中,所以在编译时期把它放到了常量池中。