那些年我们一起踩过的坑之“==”和“equals”的区别

在Java开发中,我们经常使用到“==”和“equals”去判断是否相等。那么在使用的过程中,是否有踩过一些坑,因为这些坑导致我们写的程序出现莫名其妙的bug,使我们烧脑过度debug去查找bug的位置。曾几何时,众里寻他千百度,蓦然回首,bug就是因为使用了“==”判断导致的。

那么问题来了

  • 什么情况下可以使用“==”去判断,什么情况下应该使用“equals”去判断呢

要找出答案,首先我们从JVM开始分析;在JVM中内存分为栈内存和堆内存,当我们new一个对象时候,就会调用对象的构造函数来开辟空间,将对象数据存储到堆内存中,与此同时在栈内存中生成对应的引用,当我们在后续代码中调用的时候用的都是栈内存中的引用。说到栈内存我们应该联想到Java的基本数据类型:byte,short,char,int,long,float,double,boolean;这些基本数据类型在JVM加载class文件的时候,都是存储在栈内存的,因此我们在代码中调用基本数据类型是直接用栈内存中的值。

  • 说到这下面我们通过例子来具体分析“==”和“equals”的区别
例-1
public void testCaseA(){
    String str_a = "java";
    String str_b = "java";
    //输出判断结果
    System.out.println(str_a == str_b);
    System.out.println(str_a.equals(str_b));
}

例-1中定义了两个字符串(String)类型常量:str_a、str_b,且初始化的值都是“java”;通过控制台打印出判断是否相等,输出的结果为:

true
true

从代码上看,我想大家都能想到结果都是:true,“==”和“equals”的结果是一样的。可能有人会问为什么结果是一样的呢??客官别急……咱们再举个例子对比一下,有对比才有差异,有差异才有结论嘛!!

例-2
public void testCaseB(){
    String str_c = "java";
    String str_d = new String("java");
    //输出判断结果
    System.out.println(str_c == str_d);
    System.out.println(str_c.equals(str_d));
}

例-2中的结果大家有想到了吗,情况不一样了,直接贴出结果来

false
true

看到结果,有没有疑惑呢?“==”的结果为:false,“equals”的结果为:true;现在咱们来剥洋葱一样逐一剖析原因。

  • 在例-1中定义了两个字符串常量 str_a和str_b,在JVM中常量会分配在常量池中,常量池存储在堆内存中;两个字符串常量的值都是“java”,JVM在维护常量的时候,如果一个字符串已经存在常量池中,将不再重复创建一个相同的对象,而是直接将str_b也指向“java”;因此在堆内存中字符串“java”占用的内存地址,有两个对象同时指向它(“java”),在栈内存中生成对应的一个引用,在代码中调用的也是同一个对象,这就好比一个人有两个名字,叫两个名字指的是同一个人。在进行双等号(==)运算的时候,比较的是str_a和str_b的内存地址,所以的得出的结果是:true。

  • 在例-2中定义了一个字符串常量 str_c和一个字符串变量 str_d(new出来的);两个对象的内容(“java”)是一样的;前面说到,常量JVM会分配到常量池中,存储在堆内存中;那么new来的对象呢,JVM会怎么分配呢?JVM会调用new来的对象的构造函数来开辟空间,将对象数据存储到堆内存中,与此同时在栈内存中生成对应的引用。到此大家应该有一个共同的结论了吧,str_c 和str_d 在堆内存中指向的是两个不同的内存地址,对应的在栈内存中也是一对一的两个不同的内存地址;在进行双等号(==)运算的时候,比较的是str_c 和str_c 的内存地址,而内存地址是不同,所以的得出的结果是:false。

此有人会问,为什么“equals”的结果都为:true

要找出答案,得追踪到Java中所有类的祖宗类:Object;在JAVA当中,所有的类都是继承于Object这个基类的,在Object基类中定义了一个equals的方法,这个方法的初始行为是比较对象的内存地址,但在Object的子类中这个方法有的被重写了,如String、Integer、Boolean、Date……在这些类当中equals有其自身的实现,而不再是比较类在内存中的存储地址了。到此咱再贴出String类的equals方法的实现代码:

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实现中可看出比较的是字符串内容,用的是“==”的反义“!=”来循环(while)判断字符串的每个字符是否相等,所有的字符都相等返回:true,只要有一个不等或者字符串长度不等返回:false。

  • 再来看例-3,验证例-1、例-2得出的结论
例-3
public void testCaseC(){
    String str_e = new String("java");
    String str_f = new String("java");
    //输出判断结果
    System.out.println(str_e == str_f);
    System.out.println(str_e.equals(str_f));
}

直接贴出结果:

false
true

看到这个结果,客官们是否豁然开朗了许多。

同样的基础类型:byte,short,char,int,long,float,double,boolean;对应的封装类型:Byte,Short,Character,Integer,Long,Float,Double,Boolean,这些封装类型都对equals有自己的实现,客官们可以尝试比较“==”和“equals”的区别。

曾经我在开发中踩过这个坑,两个Integer对象用“==”判断是否相等,结果出现异常的情况(bug),望各位客官们能避开这个坑。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值