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

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

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

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

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

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

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

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

3、“==”与equals()相同点:默认情况下,比较的都是变量名直接对应的值,基本数据类型变量名对应的就是实际的值,而引用数据类型所对应的是实际值的引用地址;

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

Object类中的equals():代码语言:javascript代码运行次数:0运行复制 public boolean equals(Object obj) {

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

return (this == obj);

}4、String类的equals()源代码及解析:代码语言:javascript代码运行次数:0运行复制 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类的特殊性代码演示:代码语言:javascript代码运行次数:0运行复制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));

}

}运行结果:代码语言:javascript代码运行次数:0运行复制true

false

true

true

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

“”和new关键字创建字符串的区别:咱们借助上面的代码进行说明,顺带介绍一个intern()方法:

代码语言:javascript代码运行次数:0运行复制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()方法的说明:我们平常打印一个对象,打印的是引用地址,类似下面:

代码语言:javascript代码运行次数:0运行复制函数执行前的student:

com.zibo.java.february.first.Student@4554617c

函数执行后的student:

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

代码语言:javascript代码运行次数:0运行复制 public String toString() {

return this; // 直接返回对象本身

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

代码语言:javascript代码运行次数:0运行复制 public String toString() {

return getClass().getName() + "@" + Integer.toHexString(hashCode());

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

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

代码及解析:代码语言:javascript代码运行次数:0运行复制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()方法,跟上面咱自己写的类似;


TOP