踩坑日记1——Java字符串 == 与 equals

Java字符串 == 与equals

某天正在刷力扣,遇到一道需要判断字符串是否相等的题目,死活没找到问题所在,后来才发现是==与equals的问题

// String[] words;
// String word1;
for (int i = 0; i < words.length; i++) {
    // WRONG!会始终为 false 因为两个字符串存储地址不同
    // if (words[i] == word1) ...
    if (words[i].equals(word1)) ...
}

推荐阅读:

Hollis:图说:为什么Java中的字符串被定义为不可变的

Hollis:我终于搞清楚了和String有关的那点事儿。

先来段代码

String s1 = "cygao";
String s2 = new String("cygao");

System.out.println(s1 == s2);   // false
System.out.println(s1.equals(s2));   // true

对于 Java 的基本数据类型,如 int, byte, long, boolean这类,它们进行 == 比较时,比较的是它们的值。

而对于类对象,进行 == 比较时,比较的是它们在内存中的存放地址。 Java当中所有的类都是继承于Object这个基类的,在Object中的基类中定义了一个equals的方法,这个方法的初始行为是比较对象的内存地 址,但在一些类库当中这个方法被覆盖掉了,如String,Integer,Date在这些类当中equals有其自身的实现,而不再是比较类在堆内存中的存放地址了。

例如 Integer 类重写的 equals 方法:

public boolean equals(Object obj) {
    if (obj instanceof Integer) {
        return value == ((Integer)obj).intValue();
    }
    return false;
}

String 类

public boolean equals(Object anObject) {
    if (this == anObject) {   // 如果地址一样为true
        return true;
    }
    if (anObject instanceof String) {   // 判断是否为 String 类
        // 转为字符数组,判断每个元素是否相等
        String anotherString = (String)anObject;
        int n = value.length;
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}

对于 String 类的 equals 方法,只要字符串内容相等就为 true。

而 String 类天生就是不可变类型(其它基本类型也属于不可变类,如 int、double、char等),在代码中连续创建两个相同的字符串的时候,其实会指向同一个对象。因为当一个字符串被被创建的时候,首先会去这个字符串池中查找,如果找到,直接返回对该字符串的引用。

踩坑日记1——Java字符串 == 与 equals_第1张图片

但是,如果在程序中明确声明要新创建一个字符串的话是可以在堆上重新创建一个对象的。如String s = new String("Hollis")

踩坑日记1——Java字符串 == 与 equals_第2张图片

为什么要定义成不可变类?

安全性

String被广泛的使用在其他Java类中充当参数。比如网络连接、打开文件等操作。如果字符串可变,那么类似操作可能导致安全问题。因为某个方法在调用连接操作的时候,他认为会连接到某台机器,但是实际上并没有(其他引用同一String对象的值修改会导致该连接中的字符串内容被修改)。可变的字符串也可能导致反射的安全问题,因为他的参数也是字符串。

不可变类天生线程安全

因为不可变对象不能被改变,所以他们可以自由地在多个线程之间共享。不需要任何同步处理。

缓存HashCode

Java中经常会用到字符串的哈希码(hashcode)。例如,在HashMap中,字符串的不可变能保证其hashcode永远保持一致,这样就可以避免一些不必要的麻烦。这也就意味着每次在使用一个字符串的hashcode的时候不用重新计算一次,这样更加高效。

在String类中,有以下代码:private int hash;

以上代码中hash变量中就保存了一个String对象的hashcode,因为String类不可变,所以一旦对象被创建,该hash值也无法改变。所以,每次想要使用该对象的hashcode的时候,直接返回即可。

结论

对于字符串对象,请无脑使用 equals

你可能感兴趣的:(Java)