• 从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内存分配部分的知识,也是从当中学到的。各位看官如果看完小编的文章觉得不满,觉得还比较浅显,或者还没看够的,可以去拜读一下这篇博客哟。强势推荐一波逸情公子大大!!更多的东西,小编也就不再赘述了,总之,学东西,不能浅尝辄止哟。那么,各位看官下次再见!

     

  • 相关阅读:
    在阿里云centos7.6上部署vue.js2.6前端应用
    gRPC
    Docker
    ES6、ES7、ES8、ES9、ES10
    HTTPS工作原理 HTTP协议数据结构分析 HTTP和HTTPS协议的不同之处
    SpringBean的工作原理
    Nginx负载均衡高可用---架构
    集群的负载策略
    redis 和 memcached的区别
    Django的基础教程
  • 原文地址:https://www.cnblogs.com/PHBlog/p/7294735.html
Copyright © 2020-2023  润新知