关于Java中的"=="、equals和"="

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/binbinqq86/article/details/72801706

转载请注明出处:http://blog.csdn.net/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和”=”的详细介绍到这里就结束了,如果有不明白的地方可以在下方留言,谢谢大家~

展开阅读全文

没有更多推荐了,返回首页