Java中==和equals()的区别【详解】

目录

一、Java中==和equals()的区别

1、前述

2、数据到底存在哪

在方法中声明的变量:

在类中声明的变量:

3、“==”与equals()

相同点:

不同点:

Object类中的equals():

4、String类的equals()

源代码及解析:

5、String类的特殊性

代码演示:

运行结果:

说明:

“”和new关键字创建字符串的区别:

关于toString()方法的说明:

6、手动定制equals()方法

代码及解析:

说明:


一、Java中==和equals()的区别

1、前述

正如我们所知,一个变量,如果存储的数据是基本数据类型,那么变量所指向的就是它实际的值,但如果这个变量存储的数据是引用数据类型,那么变量所指向的就是它的引用地址(实际变量值存储在堆内存中,变量名指向的是变量的堆内存地址)。我们有太多场景需要比较两个变量的值是否相等,其中最常用的就是“==”,“==”是一种浅层次的比较,比较的仅仅是变量指向的内容,要么是实际的值,要么是引用地址。对于基本数据类型,我们自然能够得到想要的结果,但对于引用数据类型,我们所比较的仅仅是引用地址,而不是实际的变量值,这不是我们所想要的。所以我们想到了equals()方法,但实际上equals()方法原本与“==”是没有什么区别的,只不过有一些类重写了它,使它能够在比较引用数据类型的数据时比较的是实际的值,下面让我们进行一个详细的探讨;

2、数据到底存在哪

在方法中声明的变量:

在方法中声明的变量是局部变量,每当程序调用方法时,系统会为该方法建立一个方法栈,其所在方法栈声明的变量就存放在方法栈中,当方法执行完毕,系统会释放该方法栈,在方法中声明的变量随着方法栈的释放而被销毁,这就是局部变量只能在方法中有效的原因;

(1)当声明的是基本数据类型的变量时,其变量名及值存储在栈内存中;

(2)当声明的是引用数据类型的变量时,其变量存储在栈内存中,变量的值存储的是所指向对象的引用地址,该变量所指向的对象存储在堆内存中;

在类中声明的变量:

在类中声明的变量是成员变量,也叫全局变量,存放在堆中,不随着某方法执行结束而被销毁;

(1)当声明的是基本数据类型的变量时,其变量名及其值放在堆内存中;

(2)当声明的是引用数据类型的变量时,其变量名及其值也放在堆内存中,变量的值存储的也是所指向对象的引用地址,该变量所指向的对象同样存储在堆内存中;

3、“==”与equals()

相同点:

默认情况下,比较的都是变量名直接对应的值基本数据类型变量名对应的就是实际的值,而引用数据类型所对应的是实际值的引用地址

不同点:

equals()是一个来自Object类的方法,可以被子类重写,所以开发人员可以定制equals()方法的实际作用,使其对变量实际的值进行比较,但"=="无法做到;

Object类中的equals():

	public boolean equals(Object obj) {
        // 我们可以看到,简单粗暴,默认的equals()方法就是通过“==”进行比较,与使用“==”直接比较没有区别
        return (this == obj);
    }

4、String类的equals()

源代码及解析:

    public boolean equals(Object anObject) {
        // 如果引用地址相同,就说明内容相等(因为是同一个字符串对象,肯定相等)
        if (this == anObject) {
            return true;
        }
        // 再判断传入的对象是否是字符串实例
        if (anObject instanceof String) {
            // 如果是字符串,向下转型成字符串,按字符串进行操作
            String anotherString = (String)anObject;
            // 先判断长度是否相等
            int n = value.length; // 这个长度是调用者的长度,value是调用者的字符数组
            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++;
                }
                // 一切没问题,说明字符串的内容相同,返回true
                return true;
            }
        }
        return false;
    }

5、String类的特殊性

代码演示:

package com.zibo.java.february.second;

public class MyStr {
    public static void main(String[] args) {
        String a = "訾博";
        String b = "訾博";
        String c = new String("訾博");
        System.out.println(a == b);
        System.out.println(a == c);
        c = c.intern();
        System.out.println(a == c);
        System.out.println(a.equals(b));
        System.out.println(a.equals(c));
    }
}

运行结果:

true
false
true
true
true

说明:

我们前面讲到,对于引用数据类型“==”比较的是内存地址,但a和b看上去好像是两个对象,却返回true,而a和c,看上去与a和b是一个意思,但返回的是false,使用equals()方法进行比较无疑都是true,下面让我们详细说明一下使用“”和new关键字创建字符串的区别;

“”和new关键字创建字符串的区别:

咱们借助上面的代码进行说明,顺带介绍一个intern()方法:

package com.zibo.java.february.second;

public class MyStr {
    public static void main(String[] args) {
        String a = "訾博"; // 此时,系统从堆内存中的字符串常量池中查找“訾博”,没查到,创建并返回引用地址给a
        String b = "訾博"; // 此时,系统从堆内存中的字符串常量池中查找“訾博”,查到了,直接返回引用地址给b
        // 也就是说,a和b指向的是同一个String对象,地址也是相同的地址
        // 使用new这种方式创建字符串,系统不会从字符串常量池进行查找,而是重新创建一个并返回引用地址给c
        // 所以此时,变量a和b的所存储的引用地址是同一个,但和c不是同一个
        String c = new String("訾博");
        System.out.println(a == b); // 所以这里比较引用地址,返回true
        System.out.println(a == c); // 这里返回false
        c = c.intern(); // 这里的intern()方法返回的还是“訾博”,只不过是通过""创建的“訾博”,做了这么一件事:
        // 从字符串常量池中查找“訾博”,如果存在就返回其地址,在这里也就意味着将a、b和c同时指向同一个“訾博”了
        System.out.println(a == c); // 所以这里变成了true
        System.out.println(a.equals(b)); // 内容肯定一致
        System.out.println(a.equals(c));
    }
}

关于toString()方法的说明:

我们平常打印一个对象,打印的是引用地址,类似下面:

函数执行前的student:
com.zibo.java.february.first.Student@4554617c
函数执行后的student:
com.zibo.java.february.first.Student@4554617c

除非我们重写类的toString()方法,对打印内容进行定制,但是打印字符串就直接打印的是值,原因也是String自己重写了toString()方法,我们来看一眼源代码:

    public String toString() {
        return this; // 直接返回对象本身
    }

我们再看一眼Object类的toString()方法:

    public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

6、手动定制equals()方法

(注意,这里是有一个问题的,重写equals()方法的时候必须重写hashCode()方法,至于为什么,我下一面博客就会专门写到,写完我把文章链接写到这!)

文章链接:为什么重写equals()方法时必须重写hashCode()方法【详解】_訾博ZiBo的博客-CSDN博客_为什么实现equals必须先实现hash方法;

代码及解析:

package com.zibo.java.february.second;

public class MyEquals {
    public static void main(String[] args) {
        Student s1 = new Student("訾博", 24);
        Student s2 = new Student("訾博", 24);
        System.out.println(s1.equals(s2)); // true
    }
}
// 这里省略了setter和getter方法
class Student{
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    // 咱们来手动定制equals()方法
    @Override
    public boolean equals(Object o) {
        // 两个对象堆内存地址相同,说明是同一个对象,自然内容也相同
        if(this == o){
            return true;
        }
        // 先判断是否是一个Student的实例对象,如果是向下转型进行比较,反之直接返回false
        if(!(o instanceof Student)){
            return false;
        }
        // 向下转型,继续判断每个值是否相等
        Student student = (Student)o;
        return this.name.equals(student.name) && this.age == student.age;
    }
}

说明:

也可以用idea自动生成equals()方法,跟上面咱自己写的类似;

你可能感兴趣的:(Java,java,字符串,==,equals)