1、String str = new String("abc")
创建了几个字符串对象?
答案:1个或者2个
Ⅰ
如果字符串常量池中已经有"abc"
存在,这种情况只需要新建 1 个对象,即 new 一个对象放在堆中。Ⅱ
当字符串常量池中没有"abc"
,此时会创建如下 2 个对象:- 一个是字符串字变量
"abc"
所对应的、驻留(intern)在字符串常量池中的实例,字符串常量池只存放实例对象的引用。 - 另一个是通过
new String()
创建并初始化的,内容与"abc"
相同的实例,在堆中。
- 一个是字符串字变量
总结: new String("xxx");
如果字符串常量池intern中没有对应的xxx
那么就需要在字符串常量池新建,然后再在堆上new 一个对象。
2、String str1 = “abc” 和 String str2 = new String(“abc”)的区别?
两者看似都是创建了一个字符串对象,但在内存中却是不一样的:
String str1= “abc”
在编译期,JVM 会去字符串常量池来查找是否存在“abc”
,如果不存在,就在字符串常量池中开辟一个空间来存储“abc”
;如果存在,就不用新开辟空间。然后在栈内存中开辟一个名字为str1
的空间,来存储“abc”
在常量池中的地址值。String str2 = new String("abc")
在编译阶段 JVM 先去字符串常量池中查找是否存在“abc”
,如果过不存在,则在常量池中开辟一个空间存储“abc”
。在运行时期,通过 String 类的构造器在堆内存中 new 了一个空间,然后字符串常量池中的“abc”
复制一份存放到堆空间中,在栈中为str2
开辟空间,存放堆中 new 出来的这个 String 对象的地址值。- 也就是说,前者在初始化的时候可能创建了一个对象,也可能一个对象也没有创建;后者因为 new 关键字,至少在内存中创建了一个对象,也有可能是两个对象。
3、Java中的128陷阱?
先来看一个例子:
public static void main(String[] args) {
Integer a=127,b=127;
Integer c=128,d=128;
System.out.println(a==b);// true
System.out.println(c==d);// false
}
为什么出现这种情况呢?
- 我们都知道
Integer
是 基本类型int
的包装类型。 - 在Java设计之初,设计者认为,开发者可能经常用到的数字范围都在100以内,而每次使用这些数字的包装类型都要开辟新空间的话,可能会占用大量的资源。
- 因此他们规定在
-128~127
之间的Integer
类型的变量,直接指向常量池中的缓存地址,不再使用new去开辟出新的空间。
执行 Integer c = 128
,相当于执行:Integer c = Integer.valueOf(128)
,基本类型自动转换为包装类的过程称为,自动装箱(autoboxing)。
这也是出现上述代码两次比较结果不同的原因!下面我们看下valueOf()
源码去体会下:
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
在Integer
中引入了 IntegerCache
来缓存一定范围的值,默认情况下范围为:-128~127
。
因此:上述代码中的 127
命中了 IntegerCache
,所以 a 和 b 是相同对象,而 128 则没有命中,所以 c 和 d 是不同对象。
那么,如果想要正确比较的话c与d的话,就需要拆箱比较,即在变量后加intValue()
方法:
public static void main(String[] args) {
Integer a=127,b=127;
Integer c=128,d=128;
System.out.println(a==b);// true
System.out.println(c.intValue()==d.intValue());// true
}
可以直接记住下面的表格,道理都是一样的: