常用类和集合框架----Object类

Object类

常用类是指JDK中提供可以直接使用的类。比如:String类,Scanner类。

Object类是Java类体系结构中的根类。

Object类是所有的Java类的父类,任何一个类,当没有使用extends指定父类时,它的父类就是Object类。

Object类的方法

clone()

克隆就是创建当前对象的一个副本。

如果想实现类的clone()功能,必须实现Cloneable接口。

深克隆和浅克隆

浅克隆:只复制对象本身,对象内的引用只会复制它的值。Object的clone()方法是浅克隆。

深克隆:不只复制对象本身,对象内的引用也会创建一个副本。

hashCode()

哈希码,哈希映射。

为了提高哈希运算效率。Object的hashCode()返回是根据内存地址生成了int值。

哈希规定:

1) 同一个对象必须多次调用,返回相同的hashCode。前提是对象的内容没有被修改。

2) 相等的对象,必须拥有相同的hashCode。

3) 不相等的对象,hashCode可以相同,也可以不同,但应该不同。

equals()

比较两个对象是否相等的。Object的equals比较的是两个对象的hashCode,作用与==是一样的,都是用来比较两个对象是否指向同一块内存地址。

比如:字符串比较时,应该比较字符序列是否一样。

String str1 = new String("Java");
String str2 = new String("Java");

str1 == str2 ; // false, 指向不同的内存空间
str1.equals(str2); // true, String类重写了equals方法,比较的是两个字符串对象的字符序列是否一样。

重写equals方法,必须要重写hashCode方法。

为什么要重写hashCode方法呢

首先, equals() 方法和 hashcode() 方法间的关系是这样的:

1、如果两个对象相同(即:用 equals 比较返回true),那么它们的 hashCode 值一定要相同;

2、如果两个对象的 hashCode 相同,它们并不一定相同(即:用 equals 比较返回 false);

上面这两句话,如果明白【散列表】的结构,就一定会很明白,这里只简单提一句:散列表同时运用了数组和链表。

《Effective java》一书中这样说到:在每个覆盖了 equals() 方法的类中,也必须覆盖 hashCode() 方法,如果不这样做的话,就会违反 Object.hashCode 的通用的约定,从而导致该类无法结合所有基于散列的集合一起正常运作,这样的集合包括HashMap,HashSet 和 HashTable。

先解释下,为什么一定要使用 hashcode() 方法:
归根结底就是为了提高程序的效率才实现了 hashcode() 方法。

程序先进行 hashcode 的比较,如果不同,那没就不必在进行 equals 的比较了,这样就大大减少了 equals 比较的次数,这对比需要比较的数量很大的效率提高是很明显的,一个很好的例子就是在集合中的使用:

我们都知道java中的List集合是有序的,因此是可以重复的,而set集合是无序的,因此是不能重复的。那么怎么能保证不能被放入重复的元素呢?单靠 equals() 方法比较的话,如果原来集合中有10000个元素,那么放入第10001个元素时,难道要将前面的所有元素都进行比较,看看是否有重复?这个效率可想而知。
因此 hashcode 就应遇而生了,java 就采用了 hash 表,利用哈希算法(也叫散列算法),就是将对象数据根据该对象的特征使用特定的算法将其定义到一个地址上,那么在后面定义进来的数据只要看对应的 hashcode 地址上是否有值,那么就用equals 比较,如果没有则直接插入,这样就大大减少了 equals 的使用次数,执行效率就大大提高了。

1. 只重写 equals() 方法,不重写 hashcode() 方法:

public class Student {
	private String name;
	private int age;
 
	public Student(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Student other = (Student) obj;
		if (age != other.age)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}
	// 省略 get,set方法...
}


执行结果:

public class hashTest {
	@Test
	public void test() {
		Student stu1 = new Student("Jimmy",24);
		Student stu2 = new Student("Jimmy",24);
		
		System.out.println("两位同学是同一个人吗?"+stu1.equals(stu2));
		System.out.println("stu1.hashCode() = "+stu1.hashCode());
		System.out.println("stu1.hashCode() = "+stu2.hashCode());
	}
}

两位同学是同一个人吗?true
stu1.hashCode() = 379110473
stu1.hashCode() = 99550389


如果重写了 equals() 而未重写 hashcode() 方法,可能就会出现两个没有关系的对象 equals 相同(因为equal都是根据对象的特征进行重写的),但 hashcode 不相同的情况。因为此时 Student 类的 hashcode 方法就是 Object 默认的 hashcode方 法,由于默认的 hashcode 方法是根据对象的内存地址经哈希算法得来的,所以 stu1 !=  stu2,故两者的 hashcode 值不一定相等。

根据 hashcode 的规则,两个对象相等其 hash 值一定要相等,矛盾就这样产生了。上面我们已经解释了为什么要使用 hashcode 算法,所以即使字面量相等,但是产生两个不同的 hashCode 值显然不是我们想要的结果。

2. 如果我们在重写 equals() 时,也重写了 hashCode() 方法:

public class Student {
	private String name;
	private int age;
	
	public Student(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + age;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Student other = (Student) obj;
		if (age != other.age)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}
	// 省略 get,set方法...
}
两位同学是同一个人吗?true
stu1.hashCode() = 71578563
stu1.hashCode() = 71578563


从 Student 类重写后的 hashcode() 方法中可以看出,重写后返回的新的 hash 值与 Student 的两个属性是有关,这样就确保了对象和对象地址之间的关联性。

一句话:实现了“两个对象 equals 相等,那么地址也一定相同”的概念!

toString()

获取对象的字符串表示形式。

Object的默认实现是类型@十六进制的HashCode

一般情况我们应该重写一个类的toString方法。

当对象需要自动转换成字符串时,调用该方法实现。

System.out.print系列方法,也会调用对象的toString方法。

finalize()

JVM的垃圾回收器,调用该方法对对象进行垃圾回收。

getClass()

获取当前对象的类的信息。反射时使用。

wait()

让当前线程进入等待状态,多线程使用。

notify()

唤醒由wait方法进入等待状态的线程中的一个,多线程使用。

notifyAll()

唤醒由wait方法进入等待状态的所有线程,多线程使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值