Java常量池理解
常量池分为两种:静态常量池和运行时常量池。
静态常量池
每个类在编译之后都会生成class文件,而class文件中就包含有静态常量池,分析class文件,如下图所示:
由于常量池中的常量的数量不是固定的,所以常量池的入口需要放置一项u2类型的数据,代表常量池的容量计数值。这里的常量池容量计数值是从1开始的。如图常量池的容量:0x001d(29)。所以共有29个常量。
运行时常量池
静态常量池编译完成之后就会保存再class文件中,当class文件中类被加载到内存中的时候,静态常量池中的常量最终都会存放到运行时常量池中,我们最终使用的都是运行时常量池的常量。
在JDK6.0及之前版本,字符串常量池是放在Perm Gen区(也就是方法区)中;
在JDK7.0版本,字符串常量池被移到了堆中了。
静态常量池和运行时常量池的区别?
看名字基本上就可以猜出来,静态常量池中的常量是不能够改变的,编译完成之后就无法改变了,但是运行时常量池却可以在运行时动态添加,如使用String的intern()方法,这个方法就可以把String中的字符串字面量添加到常量池中去。
常量池的好处
- 空间考虑
相同的常量只占用一块内存 - 时间考虑
使用==判断字符串是否相等的时候,不用像equals()方法样去比较内容,而是直接比较地址就可以了,因为常量池中保证了不同的常量的地址一定不一样。
字符串常量的理解
案例1
public class test {
public static void main(String[] args) {
String s1="abc1";//此处是数字1
String s2="abc"+1;
System.out.println(s1==s2);// 第一次比较
String s3="ab";
String s4="c";
String s5="abc";
String s6=s3+s4;
System.out.println(s5==s6);// 第二次比较
}
}
结果:
true
false
“abc”+1都是字面量,字面量在编译生成class文件时,编译器会将他们两优化到一起,放到静态常量池中去,既然已经放到静态常量池中去了,那么运行时常量池必定会存在,而s2拿的时候,运行时常量池就已经存在了abc1,所以s1和s2拿到到字符串常量是相同的。
而后面s3+s4都是字符串对象的拼接而不是字面量,所以在编译的时候不会放到常量池中去。
案例2
public class test {
public static final String s1="abc";
public static final String s2="def";
public static void main(String[] args) {
String s3=s1+s2;
String s4="abcdef";
System.out.println(s3==s4);
}
}
结果:
true
从这里可以看出,只要是引用在编译期能够直接指向有且仅有一个常量时,这个时候引用就是字符串常量
案例3
public class test {
public static final String s1;
public static final String s2;
static{
s1="abc";
s2="def";
}
public static void main(String[] args) {
String s3=s1+s2;
String s4="abcdef";
System.out.println(s3==s4);
}
}
结果:
false
这种情况和上面情况不同,因为static代码块是在运行时才会执行的,所以在编译时s1,s2虽然是final修饰,但是没有被赋值,所以仍然还是变量。
基本类型数据都有常量池吗?
不是的,Float和Double就没有实现常量池,Integer是实现了常量池的。但是Integer和字符串常量池有点区别。区别如下:
public class test {
public static void main(String[] args) {
Integer i1=10;
Integer i2=10;
Integer i3=20;
Integer i11=new Integer(10);
Integer i22=new Integer(10);
Integer i33=new Integer(20);
System.out.println(i1==i2);
System.out.println(i1==i11);
System.out.println(i11==i22);
System.out.println(i3==(i1+i2));
System.out.println(i3==(i11+i22));
System.out.println(i33==(i1+i2));
System.out.println(i33==(i11+i22));
}
}
结果:
true
false
false
true
true
true
true
其他的都好理解,主要是i33==(i1+i2),这里要特别注意:
1. 只要Integer进行了运算符运算就是进行自动拆箱操作,也就是说将包装类拆成字面量
2. 只要是一个数值字面量和一个Integer进行表达式运算,则Integer也会自动拆箱
所以这里实际上是20==20的恒等式。
这里提到的自动装箱和自动拆箱的概念大家可以看一下我的另外一篇blog。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/34786.html