深入理解String和StringBuilder

前言# 我偶尔会在问答里面看一看谁有问题,如果我知道就回答一下。今天偶然看到一个问题,去掉提问的代码部分,问题就是:

String对象的intern()方法得到的对象,为什么与String对象有时候不相等?
我看到这个问题,我也是懵逼了,今天就写个笔记复习一下。
正文# 首先,这个String对象的intern()方法是干什么用的呢?看一下源码的注释:
/** * 英文注释好长,这里就简单翻译一下 * 返回一个这个String对象的权威代表(请注意,这里返回的是代表,没说返回是自己) * 有一个字符串池,专门用来维持String对象,当intern方法被调用的时候,返回和他equals方法相同的String对象,如果没有,就把这个String添加到池中,再把这个String对象返回(也就是说这个情况,返回了自己) * * s.intern() == t.intern(),只有在s.equals(t)等于true */ public native String intern();

看来这个方法和equals关系密切,所以再看一下equals方法:
public boolean equals(Object anObject) { // 先判断是否是同一个对象 if (this == anObject) { return true; } // 判断是否是String类型 if (anObject instanceof String) { String anotherString = (String) anObject; int n = count; // 判断字符串的长度是否相等 if (n == anotherString.count) { int i = 0; // 判断每一个位置的字符是否相等 while (n-- != 0) { if (charAt(i) != anotherString.charAt(i)) return false; i++; } return true; } } return false; }

【深入理解String和StringBuilder】equals判断的仅仅的是字符串的内容,所以只要内容相同,在字符串池中都不会重复添加。
结合我们已经对字符串的了解,我们可以总结出一下几点:
1、字符串池中,只包含唯一内容的字符串。
2、字符串池,提供了相同字符串之间的复用机制,防止不同字符串创建多个对象。
这个时候突然想起来刚接触Java时的一个面试题:
String s = "abc" 和 String s = new String("abc") 的区别
这个问题大对数都能答对:
String s = "abc" 是先使用字符串池中的abc对象,如果没有创建abc并添加到字符串池中,这个逻辑和intern()方法是完全一样的, 所以这种使用方法也是推荐的使用方法。
String s = new String("abc") ,一开始的过程和第一种是一样的,同样是先从字符串池中获取,然后根据情况添加或者返回。但是new操作符,会返回一个新的String对象,也就是说,返回的String对象并不是abc。但是这种方法会出现内存的浪费,所以并不推荐使用。
为了验证我们的想法,我们运行一个小demo:
public class Main {public static void main(String[] args){ // 注意这里通过创建StringBuilder,已经创建了111,并加入到字符串池 String s1 = new StringBuilder("111").toString(); // 这里还是通过相同的方式,看看是否返回了跟s1相同的对象 String s2 = new StringBuilder("111").toString(); // 直接从字符串池中得到对象 String s3 = "111"; String s4 = "111"; System.out.println(s1.equals(s2)); System.out.println(s1 == s2); System.out.println(s3.equals(s4)); System.out.println(s3 == s4); }}

首先我们使用了两个StringBuilder来拼接字符串,看看得到的结果,然后直接从字符串池中去取,看看得到是不是同一个对象。
true false true true

第一个结果是s1.equals(s2) =true,这个没有疑问,对比内容必然是相同的。
第二个结果是s1 == s2 得到false,说明是s1和s2是相同内容的不同对象。
为什么不是相同对象呢?看一下StringBuilder的toString()方法:
@Override public String toString() { // Create a copy, don't share the array return new String(value, 0, count); }

竟然是一个new String,怪不得对象是不相同的。
第三个结果s3.equals(s4) = true, 这个没有疑问,对比内容必然是相同的。
第四个结果s3 == s4,一样是true,说明得到确实是相同的对象。
总结# 有了刚才的验证,我们基本上可以这么理解:
"abc" , 我们可以看做是单例模式,这个abc只创建一次,可以复用。
例如 String s = "abc", StringBuilder.append("abc"),实际上使用的都是同一个字符串对象。
并且我们知道了平时使用字符串的几个小细节:
1、String的equals()方法判断的内容相同,不是判断是否是相同对象。
2、StringBuilder的toString()方法,会创建新的字符串对象并返回,这个还是有优化空间的。
我们把之前学到的内容又重新复习了一遍,还找到了StringBuilder性能可以优化的地方,这次复习的收获还是非常惊喜的,最后贴出那个朋友提出的问题:
public static void main(String[] argv){
String a = new StringBuilder("aa").append("计算机").toString();
System.out.println(a.intern()==a);
String b = new StringBuilder().append("计算机").toString();
System.out.println(b.intern()==b);
String c = new String("dsd");
System.out.println(c.intern()==c);
}
为何结果是
true
false
false
而不是
false
false
fasle
?
这个问题你能够帮他解答吗?

    推荐阅读