• Java经典面试题——equals和==的区别


    在正式讲解equals和==区别之前,让我们先来了解一下对象在JVM内存中的存在形式:

    就拿这个Person对象来举例:

    Person person=new Person();
    person.name="彭于晏";
    person.height=185;
    class Person{
        String name;//名字
        int height;//身高
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    为了更好的理解对象在JVM内存中的存在形式

    我先将这个Person对象在JVM中的内存分配图画出:

    下面具体分析:

    首先,当系统执行Person person=new Person();这串代码时会在栈中定义一个变量叫做person,由于Person对象时引用类型,因此他会指向我们调用Person类的无参构造器时在堆区开辟的地址,假设为0x0011,这个地址就用于保存我们的Person对象。

    然后,由于我们在Person类中定义了两个属性name和height,因此堆区这个地址中有两个空间,一个用于存放name,一个用于存放height。height的数据类型是int,属于基本数据类型,所以他会把数据直接放在height的这个空间中。而name的数据类型是String类型,属于引用类型,因此,0x0011这个地址中存放的name实际上是一个地址,他的数据 "彭于晏"是被放在了方法区一个叫常量池的地方,这个数据在常量池也会有一个地址,假设为0x0022,则name中存放的地址就是0x0022。

    JVM内存分布介绍完毕,下面看实例

    public class Equals {
        public static void main(String[] args) {
            Person p1=new Person();
            p1.name="李华";
            Person p2=new Person();
            p2.name="李华";
            System.out.println("p1==p2的结果为:"+(p1==p2));
            System.out.println("p1.name.equals(p2.name)结果为:"+p1.name.equals(p2.name));
            System.out.println("p1.equals(p2)结果为:"+p1.equals(p2));
            
        }
    }
    class Person{
        public String name;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    运行结果如下:

    p1==p2的结果为:false
    p1.name.equals(p2.name)结果为:true
    p1.equals(p2)结果为:false

    先初步了解下==和equals的区别

    ==是一个比较运算符

    (1) ==:既可以判断基本类型,又可以判断引用类型

    (2) ==:如果判断基本类型,判断的是值是否相等

    (3) ==:如果判断引用类型,判断的是地址是否相等,即判定是不是同一个对象

    equals是Object类中的方法

    (4)equals:只能判断引用类型

    (5)默认判断地址是否相等,子类中往往重写了该方法(后面结合源码分析),用于判断内容是否相等,比如Integer,String

    对运行结果进行解析

    1.第一个输出代码System.out.println("p1p2的结果为:"+(p1p2));中,由于p1和p2都是new出来的对象,因此p1和p2都是指向了各自在堆内存中开辟的一个空间,因此p1和p2地址值不一样,输出为false;

    2.第二个输出代码System.out.println(“p1.name.equals(p2.name)结果为:”+p1.name.equals(p2.name));中,使用的是equals方法,我们知道p1.name是一个字符串,而在字符串类型中,equals方法已经被重写,比较的是p1.name和p2.name的值,而p1.name和p2.name的值都是"李华",所以输出true;

    3.第三个输出代码System.out.println(“p1.equals(p2)结果为:”+p1.equals(p2));中,p1是一个自定义类,没有重写equals方法,所以这里的equals方法仍然是来自其最高父类Object,比较的是两者的地址,由于p1和p2都是new出来的对象,因此p1和p2都是指向了各自在堆内存中开辟的一个空间,因此p1和p2地址值不一样,输出为false;

    结合源码对equals进行分析

    Object类中equals的JDK源码如下

    public boolean equals(Object obj) {

    return (this == obj);

    }

    很容易看出Object类中的equals方法判断的是否为同一对象

    但是在Object的一些子类比如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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    第一个if语句用于判断传入的对象是不是当前对象,如果是就直接返回true,

    第二个if语句用于判断传入的对象是否是String类型,如果是,则先向下转型,将传入的对象强转成String类型,然后再用if语句判断两个字符串长度是否相同,如果相同,再用while循环一个一个的比较字符,只有所以字符都一样,才返回true,否则返回false。因此String类中将equals方法重写用于去判断值是否相等。

    总结

    equals除了只能判断引用类型外,其底层实现在没有被重写的情况下和==是一致的,都是判断地址是否相等,但在被子类重写的情况下,则是去判断引用类型的内容是否相等。

    这里需要注意的是:共有两种方法可以创建引用类型

    一种是直接赋值,这种情况下,会把值直接存入常量池,不会重新分配地址,因此这时候如果赋的值相同,则不管是用==比较还是用equals比较,返回的都是true。

    另一种是用new的方式,每次new都会重新分配一个地址,所以这时候即使赋值相同,但是两者指向的地址却不同,所以用==比较仍然是会返回false,但由于引用类在继承Object类时对equals进行了改写,比较的是内容,因此在赋值相同情况下,返回true。

  • 相关阅读:
    如何设置远程服务器对本地服务器免密登录
    linux防火墙
    龙芯推出兼容IE浏览器解决方案
    每日一题:2022.11.11最后的简单模拟题
    shell的if-else判断结构
    java并发包的基石:AbstractQueuedSychronier及synchornized
    通讯录(纯C语言实现)
    零基础Linux_3(基本指令_下)目录/文件的复制移动查看打包+其它指令
    在 Python 中使用 Pillow 进行图像处理【2/4】
    Android修行手册 - 官方SearchView搭配Toolbar/样式/其他/开源项目
  • 原文地址:https://blog.csdn.net/Ajekseg/article/details/126595935