java.lang.Object

本文为 Java 源码阅读中的一节,重要程度为 1

NONGFAH 写于 2019-04-18

Class Object 是所有类的父类,但不需要显式的使用 extend 进行继承。所有对象(包括数组)都实现了这个类的方法

构造器

修饰符 方法名 描述
public Object() 默认构造器

方法区

方法列表
修饰符 返回值 方法名 描述
protected native Object clone() 创建并返回此对象的副本
protected native void finalize() 当垃圾收集器确定不再有对该对象的引用时,垃圾收集器在对象上调用该方法
public native Class< ? > getClass() 返回此 Object 的运行时类
public native int hashCode() 返回对象的哈希码值
public boolean equals(Object obj) 指示一些其他对象是否等于此
public String toString() 返回对象的字符串表示形式
public native void wait() 导致当前线程等待,直到另一个线程调用该对象的 notify()方法或 notifyAll()方法
public void wait(long timeout) 导致当前线程等待,直到另一个线程调用 notify()方法或该对象的 notifyAll()方法,或者指定的时间已过
public void wait(long timeout, int nanos) 导致当前线程等待,直到另一个线程调用该对象的 notify()方法或 notifyAll()方法,或者某些其他线程中断当前线程,或指定的时间已过
public native void notify() 唤醒正在等待对象监视器的单个线程
public native void notifyAll() 唤醒正在等待对象监视器的所有线程

clone()

protected Object clone() throws CloneNotSupportedException
创建并返回此对象的副本。
一般情况下,对于任何对象 x , x.clone() != x && x.clone().getClass() == x.getClass() && x.clone().equals(x), 即副本所引用的对象与正本的不为同一个, x 的运行时类与 y 相同,x 的实际内容与 y 相同。
如果调用 clone() 方法的类没有实现 Cloneable 接口,则抛出 CloneNotSupportedException 异常, Object类本身并不实现 Cloneable 接口 ,因此在类别为 Object 的对象上调用 clone() 方法将导致运行时抛出异常。
所有数组都被认为是实现 Cloneable 接口 ,并且数组类型T[]的clone方法的返回类型是T[] ,其中T是任何引用或原始类型。
对于没有实现 Cloneable 接口的类,clone() 方法将创建类的新实例,并将其所有字段初始化为待拷贝对象相应字段的内容,就像通过赋值一样。 这些字段的内容本身不被克隆。 因此,该方法执行该对象的“浅拷贝”,而不是“深度拷贝”操作

浅拷贝

下面代码演示浅拷贝概念

public class Person implements Cloneable{
    String name;
    Car car;
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return  super.clone();
    }
    //省略get/set/toString方法
}
public class Car{
    String brand; //品牌
    int price;
    //省略get/set/toString方法
}
//演示代码
public static void main(String[] args) throws CloneNotSupportedException{
    //创建待拷贝实例
    Car car = new Car();
    car.setBrand("长城");
    car.setPrice(10000);
    Person person =  new Person();   
    person.setName("赵子龙");
    person.setCar(car);
    //进行拷贝
    Person personCopy =(Person) person.clone();
    //验证拷贝方式是否为浅拷贝
    personCopy.getCar().setBrand("福特");
    personCopy.setName("姜子牙");
    System.out.println(person.toString());
    System.out.println(personCopy.toString());
    //可以看到,在修改副本的 car 属性时,正本的 car 也被修改为 “福特”。
    //当对象中含有可变的引用类型属性时,在复制得到的新对象对该引用类型属性内容进行修改,  
    //原始对象相应的属性内容也会发生变化,这就是"浅拷贝"的现象
}  
序列化实现深拷贝

将对象转成二进制流,然后再把二进制流反序列成一个java对象,待拷贝类及待拷贝类的引用属性(如上文中的Car) 都需要实现 Serializable 接口,以便进行序列化。
下面代码演示序列化实现深克隆

//Person类
import java.io.*;
public class Person  implements Serializable {
    private static final long serialVersionUID = -1058504248325798901L;
    String name;
    Car car;
    public Object deepClone()throws Exception{
        //序列化
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(this);
        //反序列化
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        return ois.readObject();
    }
    //省略get/set/toString方法
}
//Car类
import java.io.Serializable;
public class Car   implements Serializable {    
    private static final long serialVersionUID = 1120834063139845074L;    
    String brand; //品牌
    int price;
    //省略get/set/toString方法
}
//演示代码
public static void main(String[] args) throws CloneNotSupportedException{
    //创建待拷贝实例
    Car car = new Car();
    car.setBrand("长城");
    car.setPrice(10000);
    Person person =  new Person();
    person.setName("赵子龙");
    person.setCar(car);
    //进行拷贝
    Person personCopy =(Person) person.deepClone();
    //验证拷贝方式是否为浅拷贝
    personCopy.getCar().setBrand("福特");
    personCopy.setName("姜子牙");
    System.out.println(person.toString());
    System.out.println(personCopy.toString());
    //可以看到,在修改副本的 car 属性时,正本的 car 不再受影响。
}  
clone()方法实现深拷贝

浅拷贝发生的原因是:在拷贝过程中,对于引用属性,只进行简单的值拷贝。因此,我们可以手动把引用属性拷贝一份,然后赋值给副本 演示代码如下

public class Person implements Cloneable {
    String name;
    Car car;
    @Override
    public Object clone() throws CloneNotSupportedException {
        Person person = (Person) super.clone();
        person.car = (Car) car.clone();
        return person;
    }
    //省略get/set/toString方法
}
public class Car implements Cloneable{
    String brand; //品牌
    int price;
    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
    //省略get/set/toString方法
}
//测试代码如下
public static void main(String[] args) throws CloneNotSupportedException{
    //创建待拷贝实例
    Car car = new Car();
    car.setBrand("长城");
    car.setPrice(10000);
    Person person =  new Person();
    person.setName("赵子龙");
    person.setCar(car);
    //进行拷贝
    Person personCopy =(Person) person.clone();
    //验证拷贝方式是否为浅拷贝
    personCopy.getCar().setBrand("福特");
    personCopy.setName("姜子牙");
    System.out.println(person.toString());
    System.out.println(personCopy.toString());
    //可以看到,在修改副本的 car 属性时,正本的 car 不再受影响。  
    //但因为这种方法需要将所有的引用属性手动 clone() ,如果引用类型内部仍有引用属性,  
    //需要进行大量编码。所以一般不用这种方法。  
}  

hashCode()

public int hashCode()
返回对象的 hash 值 ,设计目的:支持类似 HashMap 等的 hash 表

重写要求:

  • 多次调用返回值相同 (没有要求在不同平台返回值相同)
  • 两个对象调用 equals() 比较的结果如果相同,则他们调用 hashCode() 的返回值也必须相同;但 equals() 结果为不同,hashCode()可以相同,也可以不同,不同可以提高哈希表操作效率

equals()

public boolean equals(Object obj)
指示一些其他对象是否等于此。

  • 自反性 :对于任何非空的参考值x , x.equals(x)应该返回true 。
  • 对称性 :对于任何非空引用值x和y , x.equals(y)应该返回true当且仅当y.equals(x)回报true 。
  • 传递性 :对于任何非空引用值x , y和z ,如果x.equals(y)回报true个y.equals(z)回报true ,然后x.equals(z)应该返回true 。
  • 一致性 :对于任何非空引用值x和y ,多次调用x.equals(y)始终返回true或始终返回false ,没有设置中使用的信息equals比较上的对象被修改。
  • 对于任何非空的参考值x , x.equals(null)应该返回false 。

在 Object 类中,该方法比较的是两个对象的地址是否相同
如果重写该方法,需要重写 hashCode() ,保持 hashCode() 的重写要求,否则会使哈希表性能下降

wait(long timeout)

public final native void wait(long timeout) throws InterruptedException;
导致当前线程等待被唤醒,直到另一个线程调用该对象的notify()方法或notifyAll()方法,或设定的时间超时。
该方法执行之前、及被唤醒继续执行时,对象必须获得 monitor 锁 (同步锁锁住的是对象,任何时刻只能有一个线程持有),在没有获得 monitor 锁的线程中执行抛出 InterruptedException 异常。执行方法会将当前线程挂起,并将当前线程放入对象的等待队列中,然后释放掉 monitor 锁使得其他线程可以得到 monitor 锁。
该类中的另两个同名重载方法 wait() 、 wait(long timeout, int nanos) 是在该方法的基础上实现的,其等待时间分别为:无限等待、等待 timeout(毫秒) + nanos(纳秒)。所以不再单独分析。(注:等待纳秒只是单纯毫秒+1,精度没那么精确)

notify()

public final void notify()
唤醒正在等待对象 monitor 锁的单个线程。
该方法执行之前,对象必须获得 monitor 锁 ,在没有获得 monitor 锁的线程中执行抛出 InterruptedException 异常。
执行该方法之后,从对象的等待队列中移除被唤醒的线程,被唤醒的进程并不会立即执行(被唤醒继续执行时,对象必须获得 monitor 锁 ),被唤醒的进程等待得到 monitor 锁后,处于可运行状态,等待操作系统调度执行。
notifyAll()与该方法类似,唤醒所有等待进程,不再单独分析。

toString()

该方法用于返回出对象的属性对应的字符串,方便查看。

getClass()

返回对象的运行时类,运行时类的具体分析放到 Class 类中进行。


导读

wait() 和 natify() 底层实现