从equals和==的区别开始

        今天我要谈的,不是具体的一个小程序的设计,也不是什么高大上的遗传算法群体智能什么什么的理念。今天我想就非常简单的一个问题,深入地解释一些东西,把自己的一些看法传达给大家。我相信,认真看下去,大家会有所收获。闲话不多谈,我们进入我们今天的正题。

        我相信很多Java的初学者都会碰到这样一个问题,那就是equals和==究竟有什么不同。小编百度了一下,在我看来,大多数网上的答案虽然讲清楚了他俩之间的区别,但是,并没有理清楚两者的本质区别。至少从Java的角度来说,并不完全正确。

        我们先来看一下下面的这一段程序:

 1 public class bool {
 2     public static void main(String[] args) {
 3         String a = "a";
 4         String b = "b";
 5         String c = "ab";
 6         String d = a+b;
 7         System.out.println(c.equals(d));
 8         System.out.println(c==d);
 9     }
10 }

       我相信,两者的输出大家都能猜到,前者输出的是true,而后者输出的是false。

        接下来,我们将具体深入到Java的源码里,去寻找我们要的答案。

        首先,我们通过F3找到println方法,对于参数类型为boolean值的参数,调用的是如下的方法:

        对于synchronized我们先不去谈,这是Java中的一个关键字,表示一种同步锁。我们看到该方法调用了两个方法,一个是print,一个是换行。我们继续按F3去查询print(x)的源码:

        走到这一步我们发现,在print(boolean b)方法里面用到了三目运算符,如果b为真,在控制台输出“true”(注意一下,这里输出的是字符串!!!),如果b为假,则在控制台输出“false”。这也就解释了,为什么我们打印的明明是一个boolean值,输出的确实true和false的字符串了。

        那么,解决了这个问题,我们回到最开始的问题,c == d和c.equals(d)究竟有什么区别?

        事实上,通过查看源码,我们能够很清楚地辨别出两者的不同。

    /**
     * Compares this string to the specified object.  The result is {@code
     * true} if and only if the argument is not {@code null} and is a {@code
     * String} object that represents the same sequence of characters as this
     * object.
     *
     * @param  anObject
     *         The object to compare this {@code String} against
     *
     * @return  {@code true} if the given object represents a {@code String}
     *          equivalent to this string, {@code false} otherwise
     *
     * @see  #compareTo(String)
     * @see  #equalsIgnoreCase(String)
     */
    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;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }

        我们可以很清楚地看到,在String类的equals方法里,在if(this == anObject)的基础上,还加了一层判断,那就是如果anObject是字符串的话,它将会比较一下两个字符串是否相同,若相同,返回true。也正是因为在方法里,多加了这么一层判断,c == d和c.equals(d)的结果会完全不同。

        事实上,看到这里,我相信,我已经向大家讲述明了==和equals的区别,那就是equals会多加上一层若是两者类型相同时的比较。但是,既然走到了这一步,我们不妨继续往下走一走。我们都知道,Java里面所有类继承自Object。事实上,String类里面的equals方法是重写来自Object里面的equals方法。通过查阅,我们查到了Object里的源码:

    /**
     * Indicates whether some other object is "equal to" this one.
     * <p>
     * The {@code equals} method implements an equivalence relation
     * on non-null object references:
     * <ul>
     * <li>It is <i>reflexive</i>: for any non-null reference value
     *     {@code x}, {@code x.equals(x)} should return
     *     {@code true}.
     * <li>It is <i>symmetric</i>: for any non-null reference values
     *     {@code x} and {@code y}, {@code x.equals(y)}
     *     should return {@code true} if and only if
     *     {@code y.equals(x)} returns {@code true}.
     * <li>It is <i>transitive</i>: for any non-null reference values
     *     {@code x}, {@code y}, and {@code z}, if
     *     {@code x.equals(y)} returns {@code true} and
     *     {@code y.equals(z)} returns {@code true}, then
     *     {@code x.equals(z)} should return {@code true}.
     * <li>It is <i>consistent</i>: for any non-null reference values
     *     {@code x} and {@code y}, multiple invocations of
     *     {@code x.equals(y)} consistently return {@code true}
     *     or consistently return {@code false}, provided no
     *     information used in {@code equals} comparisons on the
     *     objects is modified.
     * <li>For any non-null reference value {@code x},
     *     {@code x.equals(null)} should return {@code false}.
     * </ul>
     * <p>
     * The {@code equals} method for class {@code Object} implements
     * the most discriminating possible equivalence relation on objects;
     * that is, for any non-null reference values {@code x} and
     * {@code y}, this method returns {@code true} if and only
     * if {@code x} and {@code y} refer to the same object
     * ({@code x == y} has the value {@code true}).
     * <p>
     * Note that it is generally necessary to override the {@code hashCode}
     * method whenever this method is overridden, so as to maintain the
     * general contract for the {@code hashCode} method, which states
     * that equal objects must have equal hash codes.
     *
     * @param   obj   the reference object with which to compare.
     * @return  {@code true} if this object is the same as the obj
     *          argument; {@code false} otherwise.
     * @see     #hashCode()
     * @see     java.util.HashMap
     */
    public boolean equals(Object obj) {
        return (this == obj);
    }

        我们可以看到,对于所有类的基类Object来说,它的equals判断就是==。然而,我们比较的往往是一些基本的数据类型,而在这些基本类型的封装类里面的equals方法一般是重写过的。比如String类里加入了对于比较对象为String类的特别处理。

        在这里,我刻意避开了一个问题,那就是为什么c == d是错的输出。如果是之前的我,我一定会立马说,String是一个类,而不是普通的基本数据类型,类创建的对象的内容相等不代表就是一个对象。但是,之后学到更多的内容之后,我发现我想的还是比较简单了。谈一点大家就知道为什么了,既然String是一个类,为什么它的实例除了通过new关键字外,还能跟基础数据类型一样直接赋值呢?所以说,String其实是一个比较特殊的类,它的特别使得这种判断变得更为复杂。

        在这里稍微讲解一些String的特殊机制。我们都知道字符串类型的数据有这么两种常见的定义方式。1、String str = "abc";2、String str = new String("abc");。这里就设计到了String类型的内存分配问题了。简单来说,对于前者,“abc”会放入一个叫做常量池的东西里面,之后如果再有类似的定义方式,它会首先查询常量池中是否存在“abc”,若存在,则直接指向同一个“abc”,若不存在,则在常量池中加入“abc”,并指向它。也就是说,在使用第一种定义方式的时候,无论你定义多少个对象,它们都是同一个。但是通过new创建的,是一个完完全全新的对象。这也就解释了,为什么 c == d是错误的输出了。大家理解了吗?为了方便大家更加深刻地理解,小编准备了如下的一段代码,大家可以进行比对一下,这样能有更加深刻的认识:

 1 public class String_Text {
 2     public static void main(String[] args) {
 3         String str1 = "abc";
 4         String str2 = "abc";
 5         String str3 = new String("abc");
 6         String str4 = new String("abc");
 7         System.out.println(str1 == str2); // true
 8         System.out.println(str1.equals(str2));// true
 9         System.out.println(str1 == str3);// false
10         System.out.println(str1.equals(str3));// true
11         System.out.println(str3 == str4);// false
12         System.out.println(str3.equals(str4));// true
13     }
14 }

        如果说,各位看客还有什么疑问的话,这里还有一片博客推荐哟,http://weixiaolu.iteye.com/blog/1290821。这是小编一个学长大神大佬写的一篇博客,小编关于String内存分配部分的知识,也是从当中学到的。各位看官如果看完小编的文章觉得不满,觉得还比较浅显,或者还没看够的,可以去拜读一下这篇博客哟。强势推荐一波逸情公子大大!!更多的东西,小编也就不再赘述了,总之,学东西,不能浅尝辄止哟。那么,各位看官下次再见!

 

原文地址:https://www.cnblogs.com/PHBlog/p/7294735.html