Object类的常见方法有哪些
public class Object {
// native方法,返回当前运行时对象的class(子类对应可以直接调用该方法),使用final关键字修饰,不允许子类重写
// 调用方式如:Class<?> clazz = new Test().getClass();
public final native Class<?> getClass();
// 返回对象的hashcode,主要使用在哈希表中,比如 JDK 中的HashMap。
public native int hashCode();
// 比较两个对象的引用地址是否相同,String类对该方法进行了重写,用于比较字符串的值是否相同
// 一般自定义类,也会重写这个方法,比较对象的属性是否相同
public boolean equals(Object obj) {
return (this == obj);
}
// 创建并返回当前对象的拷贝,是浅拷贝
protected native Object clone() throws CloneNotSupportedException;
// 返回类的名字实例的哈希码的 16 进制字符串。建议Object所有的子类都重写这个方法。
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
// 唤醒一个在此对象监视器上等待的线程(监视器相当于就是锁的概念)。如果有多个线程在等待只会任意唤醒一个。 简洁点的说法:唤醒一个等待锁释放的线程
public final native void notify();
// 唤醒在此对象监视器上等待的所有线程(唤醒等待锁释放的所有线程)
public final native void notifyAll();
// 等待超时之后,就暂停等待,释放锁。
public final native void wait(long timeout) throws InterruptedException;
// 等待超时时间,单位是纳秒
public final void wait(long timeout, int nanos) throws InterruptedException {
if (timeout < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}
if (nanos > 0) {
timeout++;
}
wait(timeout);
}
// 等待,没有超时时间,会一直等下去
public final void wait() throws InterruptedException {
wait(0);
}
// 实例被垃圾回收器回收的时候触发的操作。
protected void finalize() throws Throwable { }
}
为什么自定义的类都要重写Object类的toString()方法
如果不重写,调用Object类的toString方法,打印出来的是对象的hash码。实际开发过程中需要记录日志来排查问题(要看下打印日志的时候是不是调用了toString方法?),一般都要看对象里面的数据到底是什么,所以一般都要重写toString方法,更加规范。
==和equals() 的区别
- ==符号
- 对于基本类型,==比较的是值是否相同
- 对于引用类型,==比较的是对象的内存地址是否相同
- equals方法
- 不能用于比较基本类型
- 比较引用类型时,所有的类默认都用Object类的equals方法,这个方式比较的还是对象的内存地址。所以,如果自定义的类没有重写Object的equals方法,则比较的还是内存地址;如果重写了,则按照重写的规则进行对比。一般重写的规则是比较两个对象的属性是否相同即可。
hashCode()有什么用,为什么要有hashcode
- hashCode()方法是获取某个对象的hash值,进而用于确定某个对象在hash表中的位置
- 比如将对象加入hashset的时候,hashset会先计算加入对象的hash值,并和已有对象的hash值比较
- 如果没有相同hash值,则将该对象直接加入hashset
- 否则:再调用equals()方法判断和该对象是否相同
- 如果相同,则不加入该对象
- 如果不同,则重新计算hash值,散列到其他位置(hash碰撞的解决方式是开放寻址法和拉链法,这个可参考文章:HashMap源码分析)
hashCode()和equals()都是比较两个对象的方法,为啥JDK提供了两个呢
- 在容器里判断是否已有某个对象的时候,用hashcode判断,效率会更高
- 但是hashcode存在hash碰撞的情况,所以即使两个对象的hash值相同,也不一定代表两个对象就一定相同,所以需要equals()方法再做进一步的判断
- 总结:
- 两个对象的hascode相同,对象不一定相同
- 两个对象的hashcode不同,则对象不相同
- 两个对象的hashcode相同且equals比较之后也相同,则两个对象一定相同
- 两个对象equals比较之后相同,则其hashcode也相同(这个必须要在重写equals方法的同时重写hashcode,否则也会出现equals相同,hashcode不同)
为什么重写equals方法的时候也要重写hashcode
如果只重写了equals方法,没有重写hashcode方法。可能会出现一种情况,一个类的两个对象往hashset加入的时候,用hashcode判断是不同的,所以都能插入hashset,但实际上两个类用equals方法比较,是完全相同的,在后续业务使用的时候会出现异常情况。
clone方法是深拷贝还是浅拷贝
结论:clone方法是浅拷贝(只有引用类型才会涉及到深浅拷贝)
- 深拷贝:将对象从内存中完整的拷贝出来,并开辟一个新的内存区域存储,副本的改变不影响源对象。
- 浅拷贝:只复制对象的引用,不复制对象本身,新旧对象共享同一块内存,副本的改变会同时修改源对象。