今天我要谈的,不是具体的一个小程序的设计,也不是什么高大上的遗传算法群体智能什么什么的理念。今天我想就非常简单的一个问题,深入地解释一些东西,把自己的一些看法传达给大家。我相信,认真看下去,大家会有所收获。闲话不多谈,我们进入我们今天的正题。
我相信很多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内存分配部分的知识,也是从当中学到的。各位看官如果看完小编的文章觉得不满,觉得还比较浅显,或者还没看够的,可以去拜读一下这篇博客哟。强势推荐一波逸情公子大大!!更多的东西,小编也就不再赘述了,总之,学东西,不能浅尝辄止哟。那么,各位看官下次再见!