转载请注明出处:http://blog.youkuaiyun.com/binbinqq86/article/details/72801706
也许从第一天学习Java我们就接触他们:”==”、equals和”=”,但是你真的了解他们的区别与作用吗?其实我也一直没有去重视他们,以为很简单,不需要去深入挖掘,导致后来在项目中犯了一个很低级的错误,产生的bug让我们组都很莫名其妙,也很严重,当时是找到原因了,但是如今在准备面试的时候又想起了那件事,感觉还是需要写一篇博客来阐述清楚,否则怕以后自己又忘记(好记性真是不如烂笔头,细节往往决定成败啊~)
其实这些东西在当初学习的时候也搞清楚了,只是由于长时间没有去在意,慢慢的就淡忘了,或者是长时间没有出现过这方面的bug,所以就没去经常复习它。好了,废话完了,开始步入正题:
“==”和equals是我们经常用来进行比较两个值或者对象的,一个是操作符,一个是方法,最多的地方应该就是if判断语句了,万物皆源于Object,下面我们就来看看这个类里面的equals方法:
public boolean equals(Object obj) {
return (this == obj);
}
很简单啊,里面也是调用的”==”来进行的具体的比较,而”==”比较的到底是什么呢?他是Java中的一个关系运算符:
- 对于八种基本数据类型(int float double…等),他比较的是两个值是否相等,比如1==2就是false
- 对于复杂对象类型,他比较的就是两个对象在内存中的地址是否相等,也就是这两个对象是否指向内存中的同一块区域
而我们经常用到的String却是比较特殊的,因为他重写了equals方法,看源码:
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String) anObject;
int n = count;
if (n == anotherString.count) {
int i = 0;
while (n-- != 0) {
if (charAt(i) != anotherString.charAt(i))
return false;
i++;
}
return true;
}
}
return false;
}
可以看到他里面也是把字符串进行拆分为基本数据类型char,再来进行比较是否相等,String中的equals比较的就是纯粹的数值相等,而两个String用”==”来进行比较呢,这就不一样了,因为他跟你创建字符串的方式有关:
String s1="abc";
String s2=new String("abc");
创建字符串无非就上面两种常用方式,他们有什么不同呢?
第一种方式系统首先会去缓冲池中找有没有一个”abc”的对象,如果没有,则新建一个,并且入池,所以此种赋值有一个好处,下次如果还有String对象也用直接赋值方式定义为“abc”, 则不需要开辟新的缓冲池空间,而仍然指向这个池中的”abc”。
第二种方式系统会先创建一个匿名对象”abc”存入缓冲池(我们暂且叫它A),然后new关键字会在堆内存中又开辟一块新的空间,然后把”abc”存进去,并且把地址返回给栈内存中的s2, 此时A对象成为了一个垃圾对象,因为它没有被任何栈中的变量指向,会被GC自动回收。
综上:我们输出s1==s2就是false,s1.equals(s2)就是true。
即使使用new关键字,第二种方式赋值,也可以使用一个java中的手动入池指令,让所创建的对象入池,以后依然可以被重复使用。(intern返回的是缓冲池中的该字符串的地址)
String s1 = new String("abc").intern();
String s2 = "abc";
这样输出的s1==s2就是true。而开发中,使用直接赋值的方式,显然效率更高。为什么String类的对象可以直接赋值?打开了String.class,有这么一段介绍:
/**
* The <code>String</code> class represents character strings. All
* string literals in Java programs, such as <code>"abc"</code>, are
* implemented as instances of this class.
* <p>
* Strings are constant; their values cannot be changed after they
* are created. String buffers support mutable strings.
* Because String objects are immutable they can be shared. For example:
* <p><blockquote><pre>
* String str = "abc";
* </pre></blockquote><p>
* is equivalent to:
* <p><blockquote><pre>
* char data[] = {'a', 'b', 'c'};
* String str = new String(data);
* </pre></blockquote><p>
* Here are some more examples of how strings can be used:
* <p><blockquote><pre>
* System.out.println("abc");
* String cde = "cde";
* System.out.println("abc" + cde);
* String c = "abc".substring(2,3);
* String d = cde.substring(1, 2);
* </pre></blockquote>
* <p>
*/
通过上面的介绍,我们可以清楚,直接赋值的话,是通过编译器在起作用,当你对”abc”没有通过new创建时,他会自动默认给你调用构造函数new String(char value[]). 不显式调用String的构造函数(通过new叫显式调用),其实JDK编译器会自动给你加上。
讲到这里相信你也清楚了”==”和equals的区别了吧~那么我们怎么实现自己的对象的equals比较呢,其实也很简单,只需要跟String一样,重写equals方法就可以了,这样就可以实现对象内容的比较,而不是地址的比较。
/**
*equlas()方法重写实例
*/
class User {
private String name;
/**
*方法描述:设置name值
*输入参数:String name
*返回类型:void
*/
public void setName(String name) {
this.name = name;
}
/**
*方法描述:获取name值
*输入参数:
*返回类型:String
*/
public String getName() {
return name;
}
/**
*方法描述:重写equals()方法
*输入参数:Object obj
*返回类型:boolean
*/
public boolean equals(Object obj) {
if(this == obj) {
return true;
}
if(null == obj) {
return false;
}
if(getClass() != obj.getClass()) {
return false;
}
User user = (User) obj;
if(!name.equals(user.name)) {
return false;
}
return true;
}
}
public class EqualsDemo {
public static void main(String[] args) {
User userA = new User();
userA.setName("王明");
User userB = new User();
userB.setName("王明");
User userC = new User();
userC.setName("王亮");
System.out.println("userA equals userB:" + userA.equals(userB));
System.out.println("userA equals userC:" + userA.equals(userC));
}
}
最终输出A和B相等,而A和C不等,这就是重写equals的例子。
另外需要注意的地方就是,我们在使用equals方法的时候,调用equals的对象一定不能为null,而括号里面比较的对象可以为null,举个例子:
String s1="abc";
String s2="";
String s3=null;
这里我们不能这样写:s3.equals(s1),因为s3对象为null,所以不能调用类成员函数euqals,其他情况都是可以的。
最后再说一下”=”,大家可能感觉这个还有什么好说的呢?不就是一个赋值运算符吗。。。是的,这就是最基本的一个赋值运算符,但是有一种情况我需要说明,就是对象在赋值的时候,举个例子吧:
List<Object> list1=new ArrayList<>();
list1.add("abc");
List<Object> list2=list1;
list2.clear();
Log.e(TAG, "onCreate: "+list1.size() );
很简单的例子,两个list集合,这个时候log里面输出的list1的size是多少呢?答案是:0
平时我们在基本数据类型赋值的时候可能没太在意这个问题,但是当到了对象赋值的时候,这里的等号的意义就是指的变量的引用了,也就是list2其时指向的是list1在内存中的那块地址,实际上两个变量指向同一块内存区域,所以当list2清空以后,list1里面也就没有数据了,这就是需要注意的地方,也是我曾经踩过的一个坑!!!那么怎么去赋值呢?这里我们可以用很多种办法:
- ArrayList b= new ArrayList(a);//利用集合自带的构造方法
- ArrayList b =(ArrayList) a.clone();//利用克隆的方法进行赋值
- b.addAll(a)
- Collections.copy(b, a);
上面这些都可以去进行list的赋值。
好了,关于”==”、equals和”=”的详细介绍到这里就结束了,如果有不明白的地方可以在下方留言,谢谢大家~