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

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

就拿这个Person对象来举例:

Person person=new Person();
person.name="彭于晏";
person.height=185;
class Person{
    String name;//名字
    int height;//身高
}

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

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

Java经典面试题——equals和==的区别_第1张图片

下面具体分析:

        首先,当系统执行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;
}

运行结果如下:

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("p1==p2的结果为:"+(p1==p2));中,由于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;
}

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

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

总结

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

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

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

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

你可能感兴趣的:(Java基础,java,开发语言)