Object类一共有12个方法:
1. registerNatives()
private static native void registerNatives();
static {
registerNatives();
}
registerNatives()是一个native方法,我们可以看出它在static块中被调用,所以Object类一被加载此方法就会被调用。这种类型的方法在java中只需要声明,它的具体实现依靠Java调用外部程序来完成。当我们new一个Object对象时,JVM需要为这个对象在堆内存分配内存空间、定义变量以及产生this指针之类的,而registerNatives()就是来进行对象这一系列初始化操作的。
2. getClass()
public final native Class<?> getClass();
getClass()方法作用返回此Object的运行时类。
3. hasCode()
public native int hashCode();
hashcode()是一个比较奇葩的类,在Object类中,只要重新定义一个Object对象,两者的hashcode值必定不相同,但hashcode方法一旦被重写就必须遵循以下原则:
- 在Java应用同一次执行过程中,同一个对象被多次调用,它们的hashCode值必须是相同的。而对于同一个应用的两次不同调用,它们的hashCode值可能相同也可能不相同。
对于两个对象来说,如果它们的equals方法比较返回为true,那么两个对象的hashcode值必然相同。 - 对于两个对象来说,如果它们的equals()方法返回为false,则它们的hashcode的值可能相等也可能不相等(这就解释了为什么在一些集合类中判断两个对象hashcode后还要使用equals()来进行二次判断,所以当集合类中对象的hashcode不相同的可能性越大,则性能越好,因为就不用在执行equals方法来判断了)。
- 对于Object对象来说,不同的Object对象的hashcode是不同的,它们返回的是对象的地址,equals返回的也是对象的地址。所以自定义类一般都会重写hashcode()和equal()方法,不然会自动继承Object类中的hashcode()和equals()方法,它们就会通过对象地址来进行判断。
4. equals()
public boolean equals(Object obj) {
return (this == obj);
}
Object类中的equals()跟hashcode()方法一样,比较的是对象的地址,但一般例如String、Integer等都会重写equals方法,如String类中的equals方法为了提高效率首先会使用“ == ”来判断,如果“ == ”判断为真,则直接返回true,如果“”为假,则先要判断两个对象的类型是否相同,如果类型相同则再比较内容。如若创建一个类未特别重写equals方法,则将默认使用Object的equals方法来进行比较,将相当于使用“”来进行比较。
5. clone()
protected native Object clone() throws CloneNotSupportedException;
Object类中的clone方法采用了protected关键字来进行修饰,其目的是为了避免我们每创建一个类就默认具有clone能力。Object中的clone方法属于浅克隆,它无法实现深拷贝,因为它不知道一个类都有哪些具体的引用类型。浅克隆即拷贝的是对象的引用,而对象的本身没有被拷贝。那么当我们使用clone方法时,将克隆对象和被克隆对象进行“==”比较,我们又会发现结果为false,而equal比较结果为true(非Object类的equals方法),这说明我们使用clone方法克隆出来的对象不是同一个对象,两个对象在堆中具有独立的空间,而此时我们可能会产生疑问为什么会说是clone方法是浅克隆。
实际上浅克隆的说法是相对于对象的属性的而言的。如果对象的属性是基本类型,当然我们就没有必要去考虑浅克隆或者深克隆了,Java默认基本数据类型的克隆是直接复制一份过来,例如上图中Person对象中的age属性,当被克隆对象的age发生改变,不会影响克隆的对象。而对于引用类型而言,浅拷贝是在两个对象之中存在指向同一位置的引用,而深克隆则会在堆内存开辟两个空间,连个对象的引用指向了两个不同的位置。之所以采用protected关键字来避免一个类默认具有浅克隆,就是因为浅克隆具有一定的不安全性。浅克隆由于克隆对象和被克隆对象都指向的同一个地址空间,所以一旦一个对象导致地址空间发生改变,势必会影响另一个对象。
那么我们如何实现深拷贝呢?
- 重写clone方法 重写clone方法实际上是从在创建一个类时Object类的clone方法不知道该类都有哪些引用类型,所以为了解决这一问题,我们都会改写clone方法,将该类的引用类型都逐层clone来做到深拷贝。
public class People implements Cloneable{
private Age age;
public Age getAge() {
return age;
}
public void setAge(Age age) {
this.age = age;
}
@Override
protected Object clone() throws CloneNotSupportedException {
People str =(People) super.clone();
str.age = (Age) str.getAge().clone();
return str;
}
}
- 对象序列化 逐层调用clone方法来实现深克隆很明显的问题就是会造成代码量偏大,所以我们一般会采用序列化和反序列化的方式来实现深克隆,被克隆对象的类必须实现Serializable序列化接口。
public class DeepCopyBySerialization {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Age a=new Age(20);
Student stu1=new Student("摇头耶稣",a,175);
//通过序列化方法实现深拷贝
ByteArrayOutputStream bos=new ByteArrayOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(bos);
oos.writeObject(stu1);
oos.flush();
ObjectInputStream ois=new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
Student stu2=(Student)ois.readObject();
System.out.println(stu1.toString());
System.out.println(stu2.toString());
}
}
6. toString()
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
tostring()方法经常会被复写,用于返回该对象的字符串表示。它将hash值转换为16进制的字符串形式。
7. notify()
public final native void notify();
唤醒此对象监视器上等待的单个线程。
8. notifyAll()
public final native void notifyAll();
唤醒此对象监视器上等待的多个线程。
9. wait(long timeout)
public final native void wait(long timeout) throws InterruptedException;
当前线程阻塞固定时常。
10. wait(long timeout , int nanos)
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);
}
当前线程阻塞固定时常并设定额外值。
11. wait()
public final void wait() throws InterruptedException {
wait(0);
}
当前线程阻塞到调用notify等方法唤醒之前。
12. fianlize()
protected void finalize() throws Throwable { }
主动释放资源