String的比较,你都搞清楚了吗

String这个使用频率极高的数据类型,每个人都能说出很多关于它的使用情景。因为String相对于int等基本类型的内在差别,也常被面试官拿来拿捏面试者。不过确实,虽然我们每天声明无数次String对象,但是对于它的那些事,还是既熟悉又陌生,有点最熟悉的陌生人的感觉。所以今天汇总一下String的常见问题,诸如比较不同形式创建方法创建的对象、不同方法创建了几个对象等问题,结合字节码、源码及分析工具来具体看一下。

先了解一下两个比较方式要比较的内容是什么。

“==”,这个执行的是if_acmpne指令,比较的是栈顶两个引用类型的值。

“equals”,这个内部会先判断“==”的结果,如果是false,继而判断值是否相等,String内部有个value的char数组,保存着字符的序列。

public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = value.length;
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            //循环比较每个char的值是否相等
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}
  • 第一种
String s1 = "abc";
String s2 = "abc";

s1 == s2 is true
s1 equals s2 is true

两个String变量,直接被“=”号赋值成相同的值。“abc”在常量区,s1和s2都指向了同一个常量区的引用。我们看看字节码。

看第5和第11行,执行了ldc(从常量池中取出常量)命令 ,可以看出两个变量都是指向的的同一个常量,所以不管比较的是引用地址还是值,都是相等的。假如我们使用VisualVM查看一下对象的情况,也是一样的结果。

  • 第二种 
String s1 = "abc";
String s2 = new String("abc");

s1 == s2 is false
s1 equals s2 is true

这里用到了Stringnew方法。new方法会在堆中创建对象,所以s2会指向在堆中分配的地址,s1仍然指向的常量,所以“==”就不再相等了,而equals比较的是值,所以依然相等。new方法已经不被推荐使用了,因为String是不可变的。看源码。

/**
 * 它是参数的一个副本. 这个构造函数已经不是必要的了,因为String是不可变的.
 *它内部的value依然和参数对象的value的地址是一样的
 */
public String(String original) {
    this.value = original.value;
    this.hash = original.hash;
}

通过VisaulVM查看结果如下:

 看到两个变量的地址已经不一样了。但是他们内部的value还是同一个对象。也就是equals依然相等。

  • 第三种
String s1 = new String("abc");
String s2 = new String("abc");

s1 == s2 is false
s1 equals s2 is true

有了第二种情况的理解,这个就好理解了,两个new,在堆上分配了两个对象,所以“==”不相等。equals比较的值依然相等。

  • 第四种
String s1 = "a"+"b"+"c";
String s2 = "abc";

s1 == s2 is true
s1 equals s2 is true

对于这个结果,是不是有点疑问,三个字符的拼接怎么会和一个完整的字符结果相等呢?JVM在这里做了个优化,直接把三个拼接的结果和“abc”处理成一个对象了。已经完全和第一种等同了。 

  •  第五种,知道了第四种情况中JVM的优化,那么这种和第二种的分析就是一样的了,可参照着理解。
String s1 = "a"+"b"+"c";
String s2 = new String("abc");

s1 == s2 is false
s1 equals s2 is true
  • 第六种
String s1 = new String("a")+new String("b")+new String("c");
String s2 = "abc";

s1 == s2 is false
s1 equals s2 is true

在这篇文章中《“+”真的可以替代StringBuilder吗》我们提到过,连加会被优化为StringBuilder,所以s1的操作就是在操作StringBuilder,前面提到的几种情况,String内部的value都是指向同一个地址的,那么因为这里用到了StringBuilder,而StringBuilder内部也有一个value,所以s1中的value其实指向的是StringBuilder的的value,因此s1s2不但两个变量的指向不一样,内部的value指向也不一样。

发布了130 篇原创文章 · 获赞 42 · 访问量 79万+

猜你喜欢

转载自blog.csdn.net/bdmh/article/details/104536285