Object类相关方法

1.equals

实现对非null对象的等价关系判断

  1. 自反性,对于任何非null引用对象x,x.equals(x)=true
  2. 对称性,对于任何非null引用对象x和y,x.equals(y)=y.equals(x)
  3. 传递性,对于任何非null引用对象x,y和z,如果x.equals(y),y.equals(z),则x.equals(z)
  4. 一致性,对于任何非null引用对象x和y,多次调用x.equals(y)得到的结果是一致的,前提是不修改对象的equals比较中使用的信息(默认equals实现是根据内存地址进行比较,即是说只要不把x或y指向另一个对象,而不是说修改对象的属性,修改属性依然是同一个对象;举例,如果person1与person2两对象通过身份证号作为信息进行比较是否相等,那么这两个对象的身份证号不能被修改)
  5. 对于任何非null引用对象x,x.equals(null)=false

2.hashCode

默认是通过内存地址转换成的一个整数

应该遵循以下约定

  1. 在一次java应用执行期间,同一对象无论调用多少次hashCode方法,都得到同一个整数值,前提是不修改对象的equals比较中使用的信息(参考equals第4点说明)
  2. 如果x.equals(y)==true,那么应该有x.hashCode()=y.hashCode()
  3. 如果x.equals(y)==false,那么应该有x.hashCode()!=y.hashCode(),我们应该有意识,对于不相等的对象,应该产生不同的hash code,这样有得于提高哈稀表的性能(可以尽量将元素打散在不同bucket中)

重写equals方法时需要一起重写hashCode,相等的对象必须有相等的hash code

  1. 是为了遵守以上equals和hashCode的约定
  2. HashSet、HashMap、HashTable中,这些容器的一些接口需要进行比较对象,是通过调用对象的hashCode方法来比较的。

3.wait

  1. 调用wait()方法前,必需要首先获取锁对象的监视器(monitor),即需要在synchronized代码块中,否则抛IllegalMonitorStateException
  2. 调用wait()方法后,当前线程挂起休眠,释放锁,并进入锁的wait set集合中,直到重新获取到锁再继续执行
  3. 调用wait()后休眠的线程A如何再次唤醒,其它线程对同一个锁调用notify方法,如果此时wait set中有<线程A,线程B,线程C>,刚好随机唤醒到线程A;或者调用了notifyAll,则A,B,C都被唤醒;唤醒的线程进入entry set等待竞争锁
  4. 线程在休眠的时候可能会被虚假唤醒(非notify),虽然这种情况机率小,程序需要防范这种情况,应该把条件判断写在while循环中
     synchronized (lock) {
        while (<条件未满足>){  //这里加个{}比较好理解,如果是两个线程之间A-B进行通讯用if不会出现什么问题,但是如果有多个线程,本来A在等待B释放锁lock,但是B释放后(条件已满足)调用notifyAll把C给唤醒了,C把条件修改成了未满足并释放锁,此时A就算获取到lock也不满足条件,如果使用if那么A会继续执行“自己写的逻辑”可能会出错,使用while的话,A被唤醒后会再一次判断条件是否满足,不满足继续wait
           lock.wait();
        }
        ...写自己的逻辑...
     }

锁池与等待池

JVM机会为每个对象维护两个集合:Entry Set和Wait Set;lock的Entry Set存储着等待竞争lock这个锁的所有线程,叫锁池,lock的Wait Set存储着执行了lock.wait()的线程,也就是等待池。
在这里插入图片描述

4.notify

  1. 调用notify方法前,必需要首先获取锁对象的监视器(monitor),即需要在synchronized代码块中
  2. 线程A调用notify后,从锁的wait set中任意唤醒其中一个线程B,B进入到锁的entry set中
  3. 虽然B被唤醒了,但并不是随后就到B执行,B仍然作为一个普通的线程,与正在等待该锁的其它线程C,D…进行竞争,只有B在竞争中获取到锁后才继续执行

5.notifyAll

  1. 调用notifyAll方法前,必需要首先获取锁对象的监视器(monitor),即需要在synchronized代码块中
  2. 线程A调用notifyAll后,从锁的wait set唤醒所有线程B,C,D并进入到锁的entry set中
  3. B,C,D被唤醒后并无特权,作为普通线程对锁进行竞争,只有在竞争中获取到锁后才继续执行

wait/notify在工作中的应用:等待通知机制(消费者-生产者模式),线程间进行通讯

6.clone

  1. Cloneable是一个标志性的接口,如果类(或父类,非Object)没实现该接口并且调用了clone()方法,则会抛CloneNotSupportedException
  2. clone是一个native方法,效率高,使用该方法时在jvm中实现field-for-field copy(浅复制),比new一个对象然后赋值好
  3. clone是一个protect方法,只能在继承链上的类中能使用,实现类override为pulic
  4. 如果类只有基本属性和不可变属性,那么可以不修改clone方法,类似Person中有Address对象属性,则需要override实现深度复制
public class Person implements Cloneable {
	public String name; // 不可变类型
	public int age; // 基本类型
	public Address addr;

	@Override
	public Object clone() throws CloneNotSupportedException { // override为public
		Person obj = (Person) super.clone(); // 使用Object.clone()本地方法进行浅复制,addr指向同一个对象,p1.addr==p2.addr
		obj.addr = (Address) addr.clone(); // 对mutable对象进行深度复制
											// ,addr指向不同对象,p1.addr!=p2.addr
		return obj;
	}
	public Person(String name, Integer age, Address addr) {
		this.name = name;
		this.age = age;
		this.addr = addr;
	}
}


public class Address implements Cloneable{

	private String country;
	private String city;

	@Override
	public Object clone() throws CloneNotSupportedException {
		return super.clone();  //jvm实现浅复制,都是基本类型的属性
	}
	public Address(String country, String city) {
		this.country = country;
		this.city = city;
	}
}

public class CloneableTest {

	public static void main(String[] args) throws CloneNotSupportedException {
		Address addr1 = new Address("中国", "广州");
		Person p1 = new Person("test", 20, addr1);
		Person p2 = (Person) p1.clone();
		System.out.println(p1 == p2); // false
		System.out.println(p1.age == p2.age); // 基本类型 true
		System.out.println(p1.name == p2.name); // 不可变类型 true
		System.out.println(p1.addr == p2.addr); // true或false主要依赖peron的clone方法实现	
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值