.equals和"=="的区别以及自定义Object

    最近在找工作,回顾Java基础的同时,做一些总结和笔记。这一篇博客,主要从三个方面去展开:第一部分是.equals和"=="的区别;第二部分是自定义Object时重写equals()方法和hashCode()方法;第三部分是从哈希表介绍为什么要重写hashCode方法。希望这篇博客对碰巧看到的朋友有所帮助吧。

一..equals()和“==”的区别

    .equals()和"=="的区别,相信有Java基础的朋友都知道。.equals方法比较的是两个对象的内容,而''=="比较的是两个对象的内存地址。

    在我们创建一个对象的时候,会分配一块内存地址。例如,我们使用如下两种方式去创建一个String对象:

String s1 = "java";
String s2 = new String("java");

    上面创建String对象的方式,区别在哪?第一种方式创建一个String对象,是非常特殊的一种情况。首先,在内存中找是否存在"java"这个对象,如果有,就让s1指向那个"java"。如果内存里没有"java",就创建一个新的对象"java"。第二种方式创建一个String对象,使用new,每次new一个对象,都会分配一块新的内存地址。也就是说,不管内存中是否存在"java"这个对象,都新创建一个对象。因此,我们如果比较s1和s2,其内容是一样的,其内存地址是不一样的。

  我们看一下下面这段代码:

public class EqualsDemo {

	public static void main(String[] args) {

		String s1 = "java";
		String s2 = new String("java");
		String s3 = s1;
		System.out.println("s1.equals(s2):" + (s1.equals(s2)));
		System.out.println("s1 == s2:" + (s1 == s2));
		System.out.println("s1 == s3:" + (s1 == s3));
	}

}

    最后的输出结果:

s1.equals(s2):true
s1 == s2:false
s1 == s3:true

    (1)s1和s2的内容是一样的,因此第一种输出为true。

    (2)s1和时的内存地址不一样,因此第二种输出为false。

    (3)s3 = s1,因为内存中已经有“java”,因此不会创建新的对象,直接指向s1的地址,因此为true。

    接下来,看一下下面一段代码:

public class EqualsDemo {

	public static void main(String[] args) {
		String s4 = "a";
		String s5 = s4 + "b";
		String s6 = "a" + "b";
		System.out.println(s5 == "ab");
		System.out.println(s6 == "ab");
	}
}

    输出结果为:

false
true

    (1)s5是变量s4的值和字符串常量“b”拼接得到的变量,与“ab”的地址不一样,因此为false。

    (2)s6 = "a"+"b",其实就是s6 = "ab",因此为true。

    java编译器可以对字符串常量直接相加的表达式进行优化,在编译时去掉加号,直接将其编译成一个这些常量相连的结果。

二.自定义Object重写equals和hashCode方法

    自定义Object的时候,我们往往会重写equals()方法和hashCode()方法。若不重写这两个方法,那么.equals比较的就是内存地址,相当于“==”。我们来看下面的例子,AObject和BObject是我自定义的两个Object,其中AObject没有重写两个方法,BObject重写了两个方法:

public class AObject {
	private String name;
	private String password;

	public AObject(String name, String password) {
		this.name = name;
		this.password = password;
	}
	
	@Override
	public int hashCode() {
		return super.hashCode();
	}

	@Override
	public boolean equals(Object obj) {
		return super.equals(obj);
	}

	@Override
	public String toString() {
		return super.toString();
	}

}
public class BObject {
	private String name;
	private String password;

	public BObject(String name, String password) {
		this.name = name;
		this.password = password;
	}

	@Override
	public int hashCode() {
		int result = 17;
		result = 31 * result + name.hashCode();
		result = 31 * result + password.hashCode();
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (obj == null) {
			return false;
		}
		if (this == obj) {
			return true;
		}
		if (this.getClass() != obj.getClass()) {
			return false;
		}
		BObject object = (BObject) obj;
		return name.equals(object.name) && password.equals(object.password);

	}

	@Override
	public String toString() {
		return "name:" + name + ";" + "password:" + password;
	}

}

    接下来,我们使用下面的java代码测试一下AObject和BObject的equals方法:

public class EqualsDemo {

	public static void main(String[] args) {
		AObject aobj1 = new AObject("Jack", "111111");
		AObject aobj2 = new AObject("Jack", "111111");
		System.out.println(aobj1.equals(aobj2));
		System.out.println(aobj1 == aobj2);

		BObject bobj1 = new BObject("Jack", "111111");
		BObject bobj2 = new BObject("Jack", "111111");
		System.out.println(bobj1.equals(bobj2));
		System.out.println(bobj1 == bobj2);
	}
}

    程序运行结果如下:

false
false
true
false

    对于AObject,因为我们没有重写他的.equals方法,因此.equals方法比较的就是对象的内存地址,因此两个结果都是false。对于BObject,因为我们重写了他的.equals方法和hashCode方法,当BObject的name和password一样的时候,我们返回true,因此第一个结果为true。而第二个比较的是两个对象的内存地址,因此为false。

三.为什么要重写hashCode方法

    为什么我们在自定义Object的时候要重写hashCode方法呢?不重写可以吗?其实,不重写也是可以的,只要我们不把我们自定义的Object作为HashTable、HashMap或者HashSet这样的hash集合的key即可。

    首先,我们复习一下哈希表的相关知识。哈希表(Hash table,也叫散列表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,哈希表不是按照equals来判断两个对象是否相等。给哈希表一个键值,他会用hashcode方法取得这个键值的哈希码也就是hashcode值,把它作为实际的索引来进行访问。

    接下来,我们看一下,假如在BObject中我们没有重写hashCode方法,也就是:

public class BObject {
	private String name;
	private String password;

	public BObject(String name, String password) {
		this.name = name;
		this.password = password;
	}

	@Override
	public int hashCode() {
		return super.hashCode();
	}

	@Override
	public boolean equals(Object obj) {
		if (obj == null) {
			return false;
		}
		if (this == obj) {
			return true;
		}
		if (this.getClass() != obj.getClass()) {
			return false;
		}
		BObject object = (BObject) obj;
		return name.equals(object.name) && password.equals(object.password);

	}

	@Override
	public String toString() {
		return "name:" + name + ";" + "password:" + password;
	}

}

    我们使用如下的java代码进行测试:

import java.util.HashMap;

public class EqualsDemo {

	public static void main(String[] args) {

		BObject bobj1 = new BObject("Jack", "111111");
		BObject bobj2 = new BObject("Jack", "111111");

		HashMap<BObject, String> hashmap = new HashMap<BObject, String>();
		hashmap.put(bobj1, "a");
		hashmap.put(bobj2, "b");
		System.out.println("bobj1.equals(bobj2):" + bobj1.equals(bobj2));
		System.out.println("size:" + hashmap.size());
		System.out.println("a:" + hashmap.get(bobj1));
		System.out.println("b:" + hashmap.get(bobj2));
	}
}

    输出结果为:

bobj1.equals(bobj2):true
size:2
a:a
b:b

    上面的结果是不是很诡异?为何两个对象的内容是相同的,但是,作为key被放进HashMap后,却被认为是两个不同的对象。这就是因为,我们重写了equals方法,因此两个对象相同是没问题的。但是,HashMap拿到的hashCode却是不一样的,因此,使用这两个对象作为key查询到的结果也是不一样的。

    我们把BObject的hashCode方法恢复到重写的状态,再次运行一下,结果如下:

bobj1.equals(bobj2):true
size:1
a:b
b:b

    在这里,对HashMap不熟悉的,可能还会有小小的疑问,为什么最后得到的结果都是b,而不是a?这是因为,使用同一个key去put值的时候,第二次是更新了原先插入的值,因此,得到的结果都是b。以上就是今天对.equals和“==”的区别和自定义Object要重写equals和hashCode方法的一些总结,如果有写的不对的地方,欢迎指正。

猜你喜欢

转载自blog.csdn.net/qq_21154101/article/details/89184234