持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第24天,点击查看活动详情
String 对象创建的基本结构
String 字符串,属于引用类型,对于引用类型而言,就是以对象的形式存在的,也就是说需要申请内存空间。
String 申请了一块内存空间,目前这块空间的名字为 name ,而在这块空间上存放的字符串是 zs 。
- 对象是如何创建的?
一般会说:对象创建就是 new + 构造方法 。
但是这里语句中并没有用到 new ,但是对象却是创建了呀?
这就是 String 类特别的地方了,它可以省略 new 的书写,同样可以完成 new 的内存申请,所以之前说了这种格式是 String 创建对象最简化的格式。
String 对象的比较
- 情况一:
String s1 = new String();
String s2 = "";
字符串对象 s1 和 s2 ,打印输出的结果是一样的,不过这只是表面现象,然而实际结构图如下:
s1,new,所有在内存空间申请 s2,没有new,去常量池寻找,如果常量池没有,就开辟空间,如果有,就直接引用
所以说,s1 申请新的地址空间,原因是使用了 new 关键字;而 s2 赋值的是字符常量值,指向的是常量池,所以执行 s1 == s2 时,判断两个对象是否指向同一个地址空间,返回的肯定是 false 。
- 情况二:
String s2 = "";
String s3 = null;
s2 虽然指向的是常量池,但这个常量池也是申请空间的,而 s3 还不是字符串对象,只是字符串变量,所以 s3 是不能访问 String 类中任何方法的。
情况三:
String s4 = new String("zs");
String s5 = "zs";
如果你已经理解了之前的两种情况,那么到了情况三,你可以很肯定当执行 s4 == s5 时,返回的结果是 false 🌸
但情况三中需要强调的,也是面试中经常会问到大家的是,String s4 = new String("zs"); 这条语句中创建了几个对象?
我们通过以下的结构图来分析一下:
从语句中我们可以看到,采用的是 String 类中提供的 public String(String original) 构造方法,那么参数完成了 String original = "zs" ,再结合图可以看到,首先,参数 original 需要申请常量池进行字符串常量值的存放,那就是一个对象是吧;然后 s4 使用 new 申请新的地址空间,来引用常量池中的值「小桃子」,那么这是第二个对象吧,所以之前的问题的答案就是该语句创建 2 个对象。
但是如果String s5="zs" 放在s4前面,那么结果就会改变,为什么?
s5 和 original 指向的是同一个地址空间,因为当 s5 申请常量池之后,original 再来申请时,系统会根据字符串常量值,进行判断,如果值一致,那么只需要将已申请地址的引用返回给 original 即可,如果值不一致,那么就给一块新的地址。因此语句执行时,参数并没有申请新的地址空间,只有 new 申请了新的地址空间,结果就是该语句创建了 1 个对象。
String 类不可变特性
String 类具有不可变特性,就是 String 类对象保存不可修改的 Unicode 字符序列,简单来说就是每次给 String 对象重新赋值就会产生一个新的对象。
name 每次重新赋值,都需要申请新的空间,这就是不可变性,所以我们也会发现字符串对象不断的重新赋值非常浪费空间,所以这也是开发人员在使用时需要注意的地方,需要合理使用字符串的赋值操作。
这里很多人有疑问:java不是有垃圾回收,怎么会浪费空间,把他回收不久ok了?
这里我们要了解,GC 主要的工作就是释放长期不使用的空间,进行释放,但是它不是实时进行释放处理,而且 GC 也是开发人员无法直接干预的,最多也就是建议回收。所以说,频繁的给字符串对象进行赋值,而不会马上将之前的空间释放,积累的越多越浪费。
来看看这个练习:
String a = "ja";
String b = "va";
String c = "ja" + "va";
String d = a + b;
String e = c;
String f = "java";
System.out.println(c == f);
System.out.println(d == f);
System.out.println(c == e);
System.out.println(d == e);
System.out.println(f == e);
答案是: c:字符串ja+字符串va,然后申请空间,赋值给c,c==java 第一个答案true
d = a + b,a 需要申请新的空间,b需要申请新的空间,当赋值给 d 时,还是需要申请新的空间,所以 d == f 结果为 false
= c,把 c 赋值给 e,对于引用类型而言,那就是 c 和 e 指向同一个空间,所以 c == e 结果肯定为 true
d 和 e 指向的空间时不一致的,所以 d == e 结果为 false
c == f 为 true,c == e 为 true,所以 f == e 肯定为 true