1、代码举例
输出结果:
为什么会出现这种情况呢?让我们了解一下底层原理。
2、底层原理图与原理介绍
String类型的数据都存放在常量池,堆中的属性只存放常量池中值的地址!
①String str1 = "abc";语句是直接创建了字符串,是构造字符串最常用的方式。这种直接赋值的方式,并没有用new关键字在堆中开辟新的地址,而是在常量池中开辟了地址。所以引用str1的内容是常量池中abc字符串的地址0x01。
②String str2 = "abc";也是直接创建了字符串。但是因为String是特殊的引用类型,其存放在常量池的内容是不可变的。在创建一个字符串对象时,会先在常量池中查询是否已存在,若不存在则再开辟新的存储空间。此时str2所引用的对象abc和str1是一样的,所以这是常量池中已经存在的内容。Str2会直接引用常量池中这个已经存在的字符串对象。所以str2的内容也是常量池中abc字符串的地址0x01。
③String str3 = new String("abc");语句用new在堆中开辟了空间,是通过创建对象来创建字符串的。str3指向堆中一个新的空间的地址0x02,此空间中存放String对象的属性值。同样的,此属性值在赋值为"abc"时,会先在常量池中查询是否已存在此内容。此时常量池中已经存在了abc,所以属性值存储的是常量池中abc的地址0x01。
④String str4 = new String("abc");语句也是用nuw在堆中开辟了空间,利用创建新对象来创建字符串。str4指向堆中一个新的空间的地址0x03,此空间中存放String对象的属性值。同样的,此属性值在赋值为"abc"时,会先在常量池中查询是否已存在此内容。此时常量池中已经存在了abc,所以属性值存储的是常量池中abc的地址0x01。
⑤如果通过创建新的对象来创建字符串,发现常量池中没有已存在的字符串;那么JVM会继续在常量池中开辟一个新的空间并把值存放在其中,而堆中的属性值为此空间的地址。
此时
str1的地址为:0.01
str2的地址为:0.01
str3的地址为:0.02
str4的地址为:0.03
而 == 对于引用类型数据比较的是地址。
所以
str1 == str2 为true
str2 == str3 为false
str3 == str4为false
⑥equals()方法和==类似,对于引用类型数据比较都是比较地址,但为什么此时打印判断都为true呢?
因为String类重写了equals()方法,将地址比较修改成了值的比较。
3、代码举例二(加深理解)
此时常量池中存放了哪些字符串呢?
String a = "a";——>发现常量池中没有"a",在常量池中创建①"a";
String b = "b";——>发现常量池中没有"b",在常量池中创建②"b";
String c = "ab";——>发现常量池中没有"ab",在常量池中创建③"ab";
String d = "a" + "b";——>发现常量池中有"a"——>发现常量池中有"b"——>"a" + "b" = "ab"——>发现常量池中有"ab";
String f = "ab" + "c";——>发现常量池中有"ab"——>发现常量池中没有"c",在常量池中创建④"c";——>"ab" + "c" = "abc",发现常量池中没有"abc",在常量池中创建⑤"abc";
String g = "abc";——>发现常量池中有"abc";
String h = "a" + "c" +"b";——>发现常量池中有"a"——>发现常量池中有"c"——>发现常量池中有"b"—— >"a" + "c" = "ac",发现常量池中没有"ac",在常量池中创建⑥"ac";——> "ac" + "b" = "acb";——>发现常量池中没有"acb",在常量池中创建⑦"acb";
所以此时常量池中存在:①"a" ②"b" ③"ab" ④"c" ⑤"abc" ⑥"ac" ⑦"acb" 七个字符串。
4、包装类型的比较
运行结果:
由此可见,包装类型和String类型一样,数据都存储在常量池中。而要创建新的包装类型的值也会先在常量池中查询是否已存在有此值的对象。包装类型也都重写了equals()方法,做值的比较。