深入理解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;
    }

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
?

这个问题你能够帮他解答吗?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值