java中 equals()方法 与 “==”的区别

时间:2021-08-09 16:23:24

摘要

java语言的 equals() 方法 与 “==” 的区别,是每个初学者都会遇到过的问题。之前自己也很清楚,可日子一长,又渐渐遗忘,于是整理出一篇博客,复习一下。

1. 基本数据类型

java的基本数据类型,也称为原始的数据类型。它们分别是:
byte, short, char, int, long, float, double, boolean .
基本数据类型进行 “==” 比较时,比较的是 它们的值 是否相同。
例如:

int a = 10;
int b = 10
System.out.println(" (a == b) = " + (a == b));

输出的结果为真。
另外需要注意的是,对于基本类型而言,它们没有 equals() 方法,大家别上当啊

2. 复合数据类型(类)

1) 当 两个对象使用 “==” 进行比较时,比较的是它们在 内存中存放的地址。 也就是说,除非是 同一个new() 出来的两个对象(内存地址相同), 否则比较的结果都是false。具体测试代码如下:

public class Main {
public static void main(String args[]) {
Worker worker1 = new Worker();
//worker2 是 worker1 的引用,同指向一个内存空间。
Worker worker2 = worker1;
System.out.println("(worker1 == worker2) : " + (worker1 == worker2));

System.out.println("-------------------------");

Worker worker3 = new Worker();
//worker3 与 worker4 都是调用new 产生的对象,不指向同一个内存空间。
Worker worker4 = new Worker();
System.out.println("(worker3 == worker4) : " + (worker3 == worker4));
}
}

class Worker {

private String name;
private double salary;

public Worker() {
this.name = "echo";
this.salary = 0.0;
}
public Worker(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
}

输出的结果:
(worker1 == worker2) : true
-------------------------
(worker3 == worker4) : false

2) 大家都知道java的所有类 都是从 java.lang.Object 这个类继承下来,而Object 类中原本就有一个 equals() 方法。首先,我们来看看源代码中 Object 类的equals() 方法。具体代码如下:

public boolean equals(Object obj) {
return (this == obj);
}

可以发现,默认情况下,当我们调用equals() 方法时,也仍然是比较两个对象 是否指向同一块内存空间。 我们 稍微 修改 上面的代码,进行测试。具体代码如下:

public class Main1 {
public static void main(String args[]) {
Worker worker1 = new Worker();
//worker2 是 worker1的引用,同指向一个内存空间。
Worker worker2 = worker1;
System.out.println("worker1.equals(worker2) : " + worker1.equals(worker2));

System.out.println("-------------------------");

Worker worker3 = new Worker();
//worker3 与 worker4 都是调用new 产生的对象,不指向同一个内存空间。
Worker worker4 = new Worker();
System.out.println("worker3.equals(worker4) : " + worker3.equals(worker4));
}
}

class Worker {
private String name;
private double salary;

public Worker() {
this.name = "echo";
this.salary = 0.0;
}
public Worker(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
}

运行结果如下:
worker1.equals(worker2) : true
-------------------------
worker3.equals(worker4) : false

这个时候,大家一定有疑问了,不是都说,equals() 方法比较的是 两个对象的内容嘛? 没错,默认的约定是这样的。对于我们自己写的类,如果想要达到这个效果,那么就要重写 equals()方法了。具体代码如下:
(可以直接使用 eclipse的工具,选择自己想要进行比较的变量,自动生成 equals()方法)。

class Worker {
private String name;
private double salary;

public Worker() {
this.name = "echo";
this.salary = 0.0;
}
public Worker(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}

//重写了Object类的 equals() 方法
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Worker other = (Worker) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (Double.doubleToLongBits(salary) != Double
.doubleToLongBits(other.salary))
return false;
return true;
}
}

接下来,看一下测试类,以及运行结果:

public class Main1 {
public static void main(String args[]) {
Worker worker1 = new Worker();
//worker2 是 worker1的引用,同指向一个内存空间。
Worker worker2 = worker1;
System.out.println("worker1.equals(worker2) : " + worker1.equals(worker2));

System.out.println("-------------------------");

Worker worker3 = new Worker();
//worker3 与 worker4 都是调用new 产生的对象,不指向同一个内存空间。
Worker worker4 = new Worker();
System.out.println("worker3.equals(worker4) : " + worker3.equals(worker4));
}
}

运行结果如下:
worker1.equals(worker2) : true
-------------------------
worker3.equals(worker4) : true

3) 在jdk的源代码中,大部分的类都自己实现了 equals() 方法,例如我们熟悉的 String,Integer,Double,Long等。大家在使用jdk提供的类时,使用到 equals()方法,最好查看一下源码,保证不出错。

3. 怪异的String

上文说了,String类重写了 equals() 方法,当调用equals()方法比较两个String对象时,比较的是其内容,那 怪异二字 从何而来呢? 且看下面代码:

public class Main2 {
public static void main(String args[]) {
String s1 = "hello world";
String s2 = "hello world";

System.out.println("s1.equals(s2) : " + s1.equals(s2));
System.out.println("(s1 == s2) : " + (s1 == s2));

}
}

运行结果如下:
s1.equals(s2) : true
(s1 == s2) : true
输出结果说明,s1 与 s2 同指向一个 String对象。

先不着急,我们再修改一下上面代码:

public class Main3 {
public static void main(String args[]) {

String s1 = "hello world";
String s2 = new String("hello world");

System.out.println("s1.equals(s2) : " + s1.equals(s2));
System.out.println("(s1 == s2) : " + (s1 == s2));

}
}

运行结果:
s1.equals(s2) : true
(s1 == s2) : false
输出结果说明,s1 与 s2 不属于同一个String对象。

为什么出现上面这个怪异的问题?
原来,程序在运行的时候会创建一个字符串缓冲池
当使用 String s2 = “hello world” ,
这样的表达是创建字符串的时候,程序首先会在这个String缓冲池中寻找相同值的对象,在第一个程序中,s1先被放到了池中,所以在s2被创建的时候,程序找到了具有相同值的 s1,将s2引用 s1所引用的 对象 “hello world”。
第二段程序中,使用了 new 操作符,它明白的告诉程序:”我要一个新的!不要旧的!”于是一个新的”hello world” Sting对象被创建在内存中。它们的值相同,但是内存地址不同,一个在池中游泳,另一个在岸边休息。

事情还没完,在看看下面这个例子。

public class Main4 {
public static void main(String[] args) {
String s1 = "hello world";
String s2 = new String("hello world");

s2 = s2.intern();

System.out.println("s1.equals(s2) : " + s1.equals(s2));
System.out.println("(s1 == s2) : " + (s1 == s2));
}
}

运行结果如下:
s1.equals(s2) : true
(s1 == s2) : true

怎么会这样???
原来,java.lang.String的intern()方法 ,例如 “abc”.intern()方法的返回值还是字符串”abc”,表面上看起来好像这个方法没什么用处。但实际上,它做了个小动作:检查字符串池里是否存在”abc”这么一个字符串,如果存在,就返回池里的字符串;如果不存在,该方法会 把”abc”添加到字符串池中,然后再返回它的引用。