Object类中最开始是一个native方法
private static native void registerNatives()
放在静态代码块中执行,被native修饰的方法,就是不用Java实现了,使用的是其他语言如C去实现,在Java中只需要进行声明即可。
接下来也是一个native方法
public final native Class<?> getClass();
返回值是Class类型,通过Class类对象可以获取到改类的方法和属性,以及构造方法等信息。
接下来是hashCode方法和equals方法
public native int hashCode();
hashcode方法是一个本地方法,Object类的hashcode方法返回当前对象的内存地址经过处理后的结构,每个对象的内存地址不一样,所以哈希码也不一样
public boolean equals(Object obj){return (this == obj);}
equals用于比较两个对象是否相等,默认的实现是使用==进行判断,只有两个对象的内存地址一样时,==的结果才会为true,因此一般要比较对象时,需要覆盖equals方法
在使用HashSet,HashMap等跟对象哈希值相关的数据结构时,需要同时覆盖hashCode和equals方法。
这两个方法的联系是:如果两个对象相等(即equals的值为true),则两个对象的hashCode必须相等,反过来则不一定成立,即如果两个对象的hashCode相等,equals方法的返回值不一定为true。
在HashSet,HashMap中,底层使用的数据结构都是数组+链表+红黑树(HashSet的底层实现基本都是依赖于HashMap),当HashMap插入数据时,先根据对象的hashCode判断出要插入的对象应该在数组中的位置,如果得到的hash值在数组中的位置已经被其他对象占有了,则这个对象和即将要插入的这两个可能是相同的(这里的相同指的是自定义的equals的结果可能是true,而不是指这两个对象的内存地址一样,因为在一般的使用场景中,假设有student这个类,这个类有studentID和name这两个属性,如果有两个对象的studentID和name的属性值完全一样,则代表了这两个对象是同一个,如果没有覆盖equals方法的话,即根据对象的内存地址进行判断两个对象是否相同,则会出现相同studentID和name的对象但是不是属于同一样对象的判断结果,相信这个结果不是我们想要的),那么这里就需要使用equals方法来进行判断了,如果equals判断为true,则是同一个对象,不允许重复插入,否则跟在该位置链表的最后面。
接下来是clone方法
protected native Object clone() throws CloneNotSupportedException;
关于clone方法,源码中的注释写了,只有实现了Cloneable接口的对象才能够调用该方法,否则会抛出CloneNotSupportedException异常。对于所有的数组类型,默认都实现了Cloneable接口。
对于普通数值类型(如int,float等)和其对应的包装类类型(如Integer,Float等),clone出的都是全新的数组,即和原来的对象没有关系。
如果要实现对象的深拷贝,则需要在让对象实现Cloneable接口,并在clone方法中把对象的引用属性全都进行复制,但是如果要拷贝的对象中,属性中所指向的对象,又有其他对象的引用的话,这样的“深拷贝”是不完全的。下面举个例子
static class Body implements Cloneable{
public Head head;
public Body() {}
public Body(Head head) {this.head = head;}
@Override
protected Object clone() throws CloneNotSupportedException {
Body newBody = (Body) super.clone();
newBody.head = (Head) head.clone();
return newBody;
}
}
static class Head implements Cloneable{
public Face face;
public Head() {}
public Head(Face face){this.face = face;}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
static class Face{}
public static void main(String[] args) throws CloneNotSupportedException {
Body body = new Body(new Head(new Face()));
Body body1 = (Body) body.clone();
System.out.println("body == body1 : " + (body == body1) );
System.out.println("body.head == body1.head : " + (body.head == body1.head));
System.out.println("body.head.face == body1.head.face : " + (body.head.face == body1.head.face));
}
对于body对象,如果仅仅只是实现了上面的clone方法,虽然拷贝出来的head确实是新的对象,但是拷贝出来的head中,两个head内部的face对象仍然是指向同一个对象,因此如果想要实现真正的深拷贝,还需要让head对象实现深拷贝,拷贝出新的face对象。(如果face对象也有其他的对象引用,也需要让face实现clone方法)
public String toString(){return getClass().getName() + “@” + Integer.toHexString(hashCode());}
toString方法打印对象名+@+哈希值
public final native void notify();和public final native void notifyAll();
public final native void wait(long timeout) throws InterruptedException;
notify,notifyAll和wait方法用于线程之间的通信。当调用对象的wait方法时,如果该线程没有持有该对象的锁的话,则会抛出java.lang.IllegalMonitorStateException异常,如果在持有锁的情况下,则该线程会释放该锁,并进行等待状态,只有其他获得该对象锁的线程调用该对象的notify或者notifyAll方法时,这些陷入等待状态的线程才会被唤醒(notify只随机唤醒某一个陷入该对象等待状态的线程,notify则是唤醒所有的陷入该对象等待状态的线程)
wait方法的timeout参数指定了至少要等待的时间才能被唤醒
public final void wait(long timeout, int nanos) 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);
}
上面可以看到,wait方法可以额外加两个参数,设置最短等待时间,第二个参数不太清楚有什么用。第一个参数以ms为单位,第二个参数以ns为单位,源码中的注释说第二个参数能更好地控制时间(???不理解)
**public final void wait() throws InterruptedException **
public final void wait() throws InterruptedException {
wait(0);
}
默认使用的wait,最短等待时间为0ms
protected void finalize() throws Throwable { }
如果类中重写了finalize方法,当该类对象被回收时,finalize方法有可能会被触发
该方法的调用时机:当垃圾回收器要宣告一个对象死亡时,至少要经过两次标记过程:如果对象在进行可达性分析后发现没有和GC Roots相连接的引用链,就会被第一次标记,并且判断是否执行finalizer( )方法,如果对象覆盖finalizer( )方法且未被虚拟机调用过,那么这个对象会被放置在F-Queue队列中,并在稍后由一个虚拟机自动建立的低优先级的Finalizer线程区执行触发finalizer( )方法,但不承诺等待其运行结束。
finalization的目的:对象逃脱死亡的最后一次机会。(只要重新与引用链上的任何一个对象建立关联即可。)但是不建议使用,运行代价高昂,不确定性大,且无法保证各个对象的调用顺序。可用try-finally或其他替代。
参考: