Java关于equals()方法和“==”逻辑运算符的区别简介

由于Java并没有像C++那样的重载运算符的机制,因此在比较类对象时,Java没有办法像C++那样通过重载==运算符来直接比较两个类对象是否相等,只能使用Object类中的equals()方法来对类对象进行比较,但是equals()方法中有一些地方值得说明。

先上代码:

import java.util.*;
public class Test {
	public static void main(String[] args) {
		Integer a1 = new Integer(10);
		Integer a2 = new Integer(10);
		System.out.println(a1==a2);
		System.out.println(a1.equals(a2));
	}
}

输出结果是:

false

true

这里应该注意到,对于类而言,创建类对象的时候,Java使用引用数据类型来指向真正的对象空间。也就是说,当我创建一个a1时,这个时候a1只是一个Integer类的引用,它储存在栈中,而new Integer(10)这行代码才是真正创建了一个Integer对象,并将这个对象设置为10,这个对象储存在堆中。当使用==运算符对比a1和a2时,结果是false,这是因为==运算符对于类对象来说比较的是引用而不是真正的对象,在栈中,a1这个引用指向的对象空间和a2引用指向的对象空间并不相同(因为a1和a2用了两次new开辟的空间),因此使用==运算符结果自然是false。这个时候要想获得true,就应该使用equals方法,这个方法才是真正到达a1和a2指向的对象空间,对这两个对象进行比较,从而得到true。

接下来,针对Integer数组来对==和equals进行说明:

public class Test {
	public static void main(String[] args) {
		Integer[] a3 = new Integer[] {
				new Integer(1), new Integer(2), new Integer(3)
		};
		Integer[] a4 = new Integer[] {
				new Integer(1), new Integer(2), new Integer(3)
		};
		System.out.println(a3==a4);
		System.out.println(a3.equals(a4));
		
	}
}

这里创建的是Integer数组,输出结果为:

false
false
本来第二个输出应该得到true,但是事实却相反。原因在于,创建数组时,数组名本身为引用,数组内的元素也是引用,拿a3来说,创建a3时,Java注意到它有1,2,3这三个元素,因此它在栈中创建一个引用,这个引用名字是a3,这个引用指向一个含有三个引用的集合,也就是一个数组,这个集合的元素名分别是a3[0],a3[1],a3[2],请注意,这三个元素都是引用,每个元素指向堆中的一个对象空间,那个空间存储的才是真正的数据。因此对a3和a4使用equals进行比较的时候仍然无法直接比较真正的对象。要想得到true,要使用另一个方法,即Arrays.equals()方法(这个方法位于util包中,在使用这个方法之前,应该添加import java.util.*;这行代码以导入这个包),这个方法不同于对象本身的方法,它接受两个数组参数,对它们进行比较。

System.out.println(Arrays.equals(a3, a4));

这行的输出结果是true,当然,也可以对数组内的元素(引用)依次进行比较:

System.out.println(a3[0].equals(a4[0]));

这样也可以得到true,这也从侧面证明了数组a3和a4内的元素,原来只不过都是引用罢了。

请注意,前面创建a3和a4,里面的元素都是使用new Integer(参数)来创建的,但是Java对基本数据类型和类对象之间一个自动包装机制,如果代码这么写:

public class Test {
	public static void main(String[] args) {
		Integer[] a5 = new Integer[] {
				1, 2, 3
		};
		Integer[] a6 = new Integer[] {
				1, 2, 3
		};
		System.out.println(a5==a6);
		System.out.println(a5.equals(a6));
		System.out.println(a5[1]==a6[1]);
		System.out.println(a5[1].equals(a6[1]));
	}
}

这里的输出是:

false
false
true
true

这个输出是令我疑惑的地方,由于自动包装机制的存在,a5的元素可以直接用整数来填充,这样一来,我知道a5是一个引用,但我无法得知a5内的元素是否是引用,如果是引用,那么第三行输出,即a5[1]==a6[1]应该得到false,如果不是引用,那么第二行输出,即a5.equals(a6)应该得到true,这个自动包装机制让我很是无法理解这里的输出,如果您看到了这里,并且知道其中玄机的话,恳请您告诉我其中的原因。

现在,我们来抛开Java自带的类,来对我们自定义的类使用equals()方法,看看会是什么样子:

class A {
	private int a;
	public A(int a) {
		this.a = a;
	}
}
public class Test {
	public static void main(String[] args) {
		A a7 = new A(10);
		A a8 = new A(10);
		System.out.println(a7==a8);
		System.out.println(a7.equals(a8));
	}
}

在这里我定义了一个简单的类A,它含有一个私有的int数据,并且通过构造器来对这个私有int进行赋值。

这里的输出结果是:

false
false
这里有一点,为什么前面对a1和a2(两者都是Integer引用)使用equals()就可以得出true,而这里却无法得出true?原因在于,位于根类Object的equals()方法在比较时默认比较的仍然是引用,在类A中,我没有对继承自Object的equals()方法进行覆盖,因此对A对象调用equals()方法时比较的仍然是引用(相当于使用了==运算符),而Integer能得出true的原因就是Java自带的包装类都覆盖了equals方法,从而能得出正确结果,对于类A来说,使equals方法进行正确行为的解决方法就是覆盖equals方法,因此可以在类A中添加方法equals():

	public boolean equals(A a) {
		if (this.a == a.a) 
			return true;
		return false;
	}

添加了equals方法后的类A可以就可以得出正确的比较结果。

 

你可能感兴趣的:(Java,equals,==)