首先来看下一面这一段代码
public class main {
public static void main(String[] args) {
Integer n1 = 127;
Integer n2 = 127;
System.out.println(n1 == n2);
Integer n3 = 128;
Integer n4 = 128;
System.out.println(n3 == n4);
Integer n5 = new Integer(127);
System.out.println(n1 == n5);
}
}
会有以下输出
第一行与第二行打印为何有不同?原因如下:
Java为了提高程序性能,为很多基本类型的包装类/字符串建立了常量池
一、常量池
1.基本特点
相同的值只存储一份,节省内存,共享访问,并且他的值也是不可被修改的,类似于static修饰的属性。
2.基本的包装类
- Boolean,Byte,Short,Integer,Long,Charater,Float,Double
3.建立常量池的包装类
Float与Double没有常量池
- Boolean:true,false
- Byte:-128~127
- Character:0~127
- Short、Int、Long:-128~127
二、回到例子
由于127在常量池内,故在定义n1与n2时,除非像n5,显示地创建一个新的对象,否则他们都指向同一块内存。
- 注意:
常量式赋值创建,放在栈内存(常量化):例如n1、n2
new对象行创建,放在堆内存(不会常量化):n5
这也解释了为什么n1与n5引用不同,因为他们创建对象存放的位置不同。因为堆的容量大,读取慢,栈的容量小,读取快,故常量化可以加速(一般new出来的对象占用内存都比较大,故显现实地使用new来创建Interger时,ide会保守地不常量化)。
而面对String类型时,也会进行同样地优化
public class main {
public static void main(String[] args) {
String s1 = "abc";
String s2 = "abc";
String s3 = "ab" + "c"; // 都是常量,编译器优化,下同
String s4 = "a" + "b" + "c";
System.out.println(s1 == s2); // true
System.out.println(s1 == s3); // true
System.out.println(s1 == s4); // true
}
}
三、自动拆箱与自动装箱
下面自动装箱的意思为:对10进行包装成为类,而i2的引用指向这个存放在栈上的对象,变为Integer类型。而自动拆箱则与之相对,将包装去掉,变回int类型。
public class main {
public static void main(String[] args) {
int i1 = 10;
Integer i2 = 10; // 自动装箱且i2在栈内存
System.out.println(i1 == i2); // true
// 自动拆箱,基本数据类型与包装类进行比较,包装类自动拆箱
Integer i3 = new Integer(10); // 堆内存
System.out.println(i1 == i3); // true
// 自动拆箱,基本数据类型与包装类进行比较,包装类自动拆箱
System.out.println(i2 == i3); // false
// 两个对象比较,比较他们的地址
Integer i4 = new Integer(5);
Integer i5 = new Integer(5);
System.out.println(i1 == i4+i5); // true
System.out.println(i2 == i4+i5); // true
System.out.println(i3 == i4+i5); // true
// 自动拆箱,基本数据类型与包装类进行比较,包装类自动拆箱
Integer i6 = i4 + i5;
// + 操作使i4、i5自动拆箱,得到10,故i6 == i2
System.out.println(i1 == i6); // true
System.out.println(i2 == i6); // true
System.out.println(i3 == i6); // false
}
}
其中,i4+i5的结果为int类型。
而对于String类型也有相类似的操作
public class main {
public static void main(String[] args) {
String s0 = "abcdef"; // 栈内存
String s1 = "abc"; // 栈内存且s2、s1指向相同的内存
String s2 = "abc"; // 栈内存
String s3 = new String("abc"); // 堆内存
String s4 = new String("abc"); // 堆内存
System.out.println(s1 == s2); // true 常量池
System.out.println(s1 == s3); // false 一个栈内存、一个堆内存
System.out.println(s3 == s4); // false 两个堆内存
String s5 = s1 + "def"; // 涉及到变量,编译器不优化
String s6 = "abc" + "def"; // 都是常量,编译器自动优化为"abcdef"
String s7 = "abc" + new String("def"); // 涉及到new对象,编译器不优化
System.out.println(s5 == s6); // false
System.out.println(s5 == s7); // false
System.out.println(s6 == s7); // false
System.out.println(s0 == s6); // true
}
}
从s5的例子也能看出,String类型为immutable的