Object类源码学习

Object 源码

public class Object {

    private static native void registerNatives();

    static {
        registerNatives();
    }

    /**
     * 获取类的字节码对象
     */
    public final native Class<?> getClass();

    /**
     * 调用对象的notify()方法能够唤醒一个正在等待该对象的线程,
     * 如果有多个线程都在等待这个对象,则只能唤醒其中一个线程;
     */
    public final native void notify();

    /**
     * 够唤醒所有正在等待这个对象的线程
     */
    public final native void notifyAll();

    /**
     * 堵塞当前线程,timeout毫秒后自动唤醒当前线程。
     * 当timeout为0时,表示不会自动唤醒线程。
     */
    public final native void wait(long timeout) throws InterruptedException;

    /**
     *阻塞当前线程
     */
    public final void wait() throws InterruptedException {
        wait(0);
    }
    /**
     * 堵塞当前线程,timeout毫秒后自动唤醒当前线程。nanos纳秒
     * 当timeout为0时,表示不会自动唤醒线程。
     */
    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);
    }
    
    /**
     * 获取hashCode
     */
    public native int hashCode();

    /**
     * 对象比较是否相等
     */
    public boolean equals(Object obj) {
        return (this == obj);
    }

    /**
     * 对象转出字符串
     */
    public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

    /**
     *当垃圾回收器确定回收该对象时自动调用
     */
    protected void finalize() throws Throwable { }

    /**
     * 克隆对象
     */
    protected native Object clone() throws CloneNotSupportedException;
    
}

一、getClass方法

getClass() 是本地方法,并且为final方法,无法被重写。

实例1:
public class App {
    public static void main(String[] args) {

        Class clazz1 = new Person().getClass();
        Class clazz2 = new Person().getClass();
        Class clazz3 = Person.class;

        System.out.println(clazz1 == clazz2);//true
        System.out.println(clazz1 == clazz3);//true

        System.out.println(Object.class == (Object)(new Person()).getClass());//false
        System.out.println(Person.class == (Object)(new Person()).getClass());//true

    }
}

class Person{

}

结论:

1、  一个类可以有很多实例,但是它的字节对象只有一个。字节对象是类级别的,因此只有一个。

2、   一个对象可以被强转成其他对象但是其字节码保持不变。

二、equals和hashCode方法

在Java规范中,equals()方法的生成要遵循如下规则:x、y、z都非空

1、自反性:  x.equals(x) 一定是true;

2、对null:   x.equals(null)一定是false;

3、对称性:  x.equals(y) ==y.equals(x);

4、传递性:  如果 x.equals(y) 返回 true,并且y.equals(z) 返回 true,那么 x.equals(z) 应返回 true;

5、一致性:  如果 equals 比较中所用的信息没有被修改,那么 x.equals(y) 比较的结果始终不会改变。

hashCode 和equals的关系:

1、  重写equals必须重写hashCode,这里就不再多说了,百度一下就知道;

2、X.equals(Y)==true   => X.hashCode()==Y.hashCode();

3、X.hashCode()==Y.hashCode()   ≠> X.equals(Y)==true;

4、X.equals(Y)==false   ≠>X.hashCode()!=Y.hashCode()。

 实例2:

public class App {
    public static void main(String[] args) {
        Person person = new Person();
        Student student = new Student();

        System.out.println(person.equals(student));//true
        System.out.println(student.equals(person));//false

    }
}

class Person {
    private String name;
    private char firstName;
    private int height;
    private double weight;
    private boolean sex;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Person)) return false;

        Person person = (Person) o;

        if (firstName != person.firstName) return false;
        if (height != person.height) return false;
        if (Double.compare(person.weight, weight) != 0) return false;
        if (sex != person.sex) return false;
        return name != null ? name.equals(person.name) : person.name == null;

    }

    @Override
    public int hashCode() {
        int result;
        long temp;
        result = name != null ? name.hashCode() : 0;
        result = 31 * result + (int) firstName;
        result = 31 * result + height;
        temp = Double.doubleToLongBits(weight);
        result = 31 * result + (int) (temp ^ (temp >>> 32));
        result = 31 * result + (sex ? 1 : 0);
        return result;
    }
}

class Student extends Person {
    private String school;

    public String getSchool() {
        return school;
    }

    public void setSchool(String school) {
        this.school = school;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Student)) return false;
        if (!super.equals(o)) return false;

        Student student = (Student) o;

        return school != null ? school.equals(student.school) : student.school == null;
    }

    @Override
    public int hashCode() {
        int result = super.hashCode();
        result = 31 * result + (school != null ? school.hashCode() : 0);
        return result;
    }
}

这个例子中的equals和hashCode方法是编辑器帮我自动生成的,从中可以学到equals和hashCode的生成规则。main方法中,person.equals(student)==true而student.equals(person))==false,这显然违反了equals规则中的对称性。

实例3:

public class App {
    public static void main(String[] args) {
        Person person = new Person();
        Student student = new Student();

        System.out.println(person.equals(student));//false
        System.out.println(student.equals(person));//false

    }
}

class Person {
    private String name;
    private char firstName;
    private int height;
    private double weight;
    private boolean sex;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Person person = (Person) o;

        if (firstName != person.firstName) return false;
        if (height != person.height) return false;
        if (Double.compare(person.weight, weight) != 0) return false;
        if (sex != person.sex) return false;
        return name != null ? name.equals(person.name) : person.name == null;

    }

    @Override
    public int hashCode() {
        int result;
        long temp;
        result = name != null ? name.hashCode() : 0;
        result = 31 * result + (int) firstName;
        result = 31 * result + height;
        temp = Double.doubleToLongBits(weight);
        result = 31 * result + (int) (temp ^ (temp >>> 32));
        result = 31 * result + (sex ? 1 : 0);
        return result;
    }
}

class Student extends Person {
    private String school;

    public String getSchool() {
        return school;
    }

    public void setSchool(String school) {
        this.school = school;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        if (!super.equals(o)) return false;

        Student student = (Student) o;

        return school != null ? school.equals(student.school) : student.school == null;

    }

    @Override
    public int hashCode() {
        int result = super.hashCode();
        result = 31 * result + (school != null ? school.hashCode() : 0);
        return result;
    }
}

这里的equals和hashCode方法同样也是编辑器自动生成的。这次并没有违反了equals规则中的对称性。实例2和实例3结果的差异,是由equals方法中instanceof和getClass导致的。

实例4:

public class App {
    public static void main(String[] args) {
        Person person = new Person();
        Student student = new Student();

        System.out.println(person.equals(student));//true
        System.out.println(student.equals(person));//true

    }
}

class Person {
    private String name;
    private char firstName;
    private int height;
    private double weight;
    private boolean sex;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Person)) return false;

        Person person = (Person) o;

        if (firstName != person.firstName) return false;
        if (height != person.height) return false;
        if (Double.compare(person.weight, weight) != 0) return false;
        if (sex != person.sex) return false;
        return name != null ? name.equals(person.name) : person.name == null;

    }

    @Override
    public int hashCode() {
        int result;
        long temp;
        result = name != null ? name.hashCode() : 0;
        result = 31 * result + (int) firstName;
        result = 31 * result + height;
        temp = Double.doubleToLongBits(weight);
        result = 31 * result + (int) (temp ^ (temp >>> 32));
        result = 31 * result + (sex ? 1 : 0);
        return result;
    }
}

class Student extends Person {
    private String school;

    public String getSchool() {
        return school;
    }

    public void setSchool(String school) {
        this.school = school;
    }

}

这里的Student类不在重写equals和hashCode方法。运行结果同样没有违反了equals规则中的对称性。


实例2中,因为它违反了equals的对称性,就不用去探究了。

实例3和实例4都是正确的,到底选哪一种方式是由我们的业务场景决定的。

实例3中,equals中使用getClass,这种equsls比较是严格的;创建对象使用的类必须是同一个类,才有可能返回true;如果父类的equals中使用了getClass,子类可以覆盖父类的equals方法,如果子类覆盖父类equals方法,那么equals方法同样要使用getClass。

实例4中,equals中使用instanceof,这种equals比较是宽松的;创建对象使用的类不是同一个类,也有可能返回true。如果抽象父类的equals中使用了instanceof,子类覆盖父类的equals方法,将会出现实例2中的错误;如果抽象父类的equals中使用了instanceof,一部分子类覆盖父类的equals方法,一部分子类没有覆盖,将会出现实例2中的错误。如果抽象父类的equals中使用了instanceof,全部子类都覆盖父类的equals方法,意味着抽象父类的equals方法将永远不可能被调用,这种方式是可以的,Spring中org.springframework.core.io.AbstractResource 抽象父类就是采用这种方式


三、wait、notify和notifyAll方法

首先了解一下线程的状态:

线程从创建、运行到结束总是处于下面五个状态之一:新建状态、就绪状态、运行状态、阻塞状态及死亡状态。

 

1.新建状态

当用new操作符创建一个线程时。此时程序还没有开始运行线程中的代码。

2.就绪状态

一个新创建的线程并不自动开始运行,要执行线程,必须调用线程的start()方法。当线程对象调用start()方法即启动了线程,start()方法创建线程运行的系统资源,并调度线程运行run()方法。当start()方法返回后,线程就处于就绪状态。

处于就绪状态的线程并不一定立即运行run()方法,线程还必须同其他线程竞争CPU时间,只有获得CPU时间才可以运行线程。因为在单CPU的计算机系统中,不可能同时运行多个线程,一个时刻仅有一个线程处于运行状态。因此此时可能有多个线程处于就绪状态。对多个处于就绪状态的线程是由Java运行时系统的线程调度程序来调度的。

3.运行状态(running)

当线程获得CPU时间后,它才进入运行状态,真正开始执行run()方法。

4.阻塞状态(blocked)

线程运行过程中,可能由于各种原因进入阻塞状态:

①   线程通过调用sleep方法进入睡眠状态;

②   线程调用一个在I/O上被阻塞的操作,即该操作在输入输出操作完成之前不会返回到它的调用者;

③   线程试图得到一个锁,而该锁正被其他线程持有;

④   线程在等待某个触发条件;

 所谓阻塞状态是正在运行的线程没有运行结束,暂时让出CPU,这时其他处于就绪状态的线程就可以获得CPU时间,进入运行状态。

 堵塞状态是前述四种状态中最有趣的,值得我们作进一步的探讨。线程被堵塞可能是由下述五方面的原因造成的:

(1) 调用sleep(毫秒数),使线程进入"睡眠"状态。在规定的时间内,这个线程是不会运行的。

(2) 用suspend()暂停了线程的执行。除非线程收到resume()消息,否则不会返回"可运行"状态。

(3) 用wait()暂停了线程的执行。除非线程收到nofify()或者notifyAll()消息,否则不会变成"可运行"(是的,这看起来同原因2非常相象,但有一个明显的区别是我们马上要揭示的)。

(4) 线程正在等候一些IO(输入输出)操作完成。

(5) 线程试图调用另一个对象的"同步"方法,但那个对象处于锁定状态,暂时无法使用。

 5.死亡状态(dead)

有两个原因会导致线程死亡:

①run方法正常退出而自然死亡;

②一个未捕获的异常终止了run方法而使线程猝死;


enum Type {
    sleep, wait, notify, notifyAll
}

class ThreadTest extends Thread {
    private Object lock;
    private Type type;

    public ThreadTest(Object lock, Type type, String threadName) {
        super(threadName);
        this.lock = lock;
        this.type = type;
    }

    @Override
    public void run() {
        synchronized (lock) {
            System.out.println(Thread.currentThread().getName() + "获取了锁");
            content();
            System.out.println(Thread.currentThread().getName() + "释放了锁");
        }
    }

    private void content() {
        try {
            if ("sleep".equals(type.name())) {
                Thread.sleep(5000L);
            } else if ("wait".equals(type.name())) {
                lock.wait(10000);
                for (int i=1;i<=3;i++){
                    Thread.sleep(5L*i);
                    System.out.println(Thread.currentThread().getName() + "running");
                }
            } else if ("notify".equals(type.name())) {
                lock.notify();
            } else {
                lock.notifyAll();
            }
        } catch (Exception e) {
        }
    }
}

实例5:

public class Test {
    public static Object object = new Object();

    public static void main(String[] args) throws Exception {
        Object ziYuan = new Object();//资源

        ThreadTest threadSleep = new ThreadTest(ziYuan, Type.sleep, "threadSleep");
        ThreadTest threadWait1 = new ThreadTest(ziYuan, Type.wait, "threadWait1");
        ThreadTest threadWait2 = new ThreadTest(ziYuan, Type.wait, "threadWait2");
        ThreadTest threadNotify = new ThreadTest(ziYuan, Type.notify, "threadNotify");
        ThreadTest threadNotifyAll = new ThreadTest(ziYuan, Type.notifyAll, "threadNotifyAll");

        threadWait1.start();
        threadWait2.start();
        Thread.sleep(4000);
        threadNotify.start();

    }
}

结果:

threadWait1和threadWait2由于调用了wait方法使线程进入堵塞状态,threadNotify调用notify方法将threadWait1,threadWait2其中的一个线程唤醒进入就绪状态。剩下的线程自一直在堵塞状态。

实例6

public class Test {
    public static Object object = new Object();

    public static void main(String[] args) throws Exception {
        Object ziYuan = new Object();//资源

        ThreadTest threadSleep = new ThreadTest(ziYuan, Type.sleep, "threadSleep");
        ThreadTest threadWait1 = new ThreadTest(ziYuan, Type.wait, "threadWait1");
        ThreadTest threadWait2 = new ThreadTest(ziYuan, Type.wait, "threadWait2");
        ThreadTest threadNotify = new ThreadTest(ziYuan, Type.notify, "threadNotify");
        ThreadTest threadNotifyAll = new ThreadTest(ziYuan, Type.notifyAll, "threadNotifyAll");

        threadWait1.start();
        threadWait2.start();
        Thread.sleep(4000);
        threadNotifyAll.start();

    }
}

结果:

threadNotifyAll唤醒所有线程进入就绪状态。

实例7:

public class Test {
    public static Object object = new Object();

    public static void main(String[] args) throws Exception {
        Object ziYuan = new Object();//资源

        ThreadTest threadSleep = new ThreadTest(ziYuan, Type.sleep, "threadSleep");
        ThreadTest threadWait1 = new ThreadTest(ziYuan, Type.wait, "threadWait1");
        ThreadTest threadWait2 = new ThreadTest(ziYuan, Type.wait, "threadWait2");
        ThreadTest threadNotify = new ThreadTest(ziYuan, Type.notify, "threadNotify");
        ThreadTest threadNotifyAll = new ThreadTest(ziYuan, Type.notifyAll, "threadNotifyAll");

        threadSleep.start();
        threadNotify.start();

    }
}

结果:

线程sleep方法不会释放资源,而wait方法会释放资源。


四、finalize方法

当jvm进行垃圾回收的时候会自动调用finalize方法

实例8:

public class App {
    public static void main(String[] args) {
        new Person();
        System.gc();
        while (true);
    }
}

class Person {
    @Override
    protected void finalize() throws Throwable {
       System.out.println("Person.finalize()");
    }
}

五、clone方法


我们创建的类默认继承了Object,但是clone方法不能直接使用,创建的类还必须要实现Cloneable接口,该接口无任何方法。为了让创建的对象方便调用clone方法,创建的类最好覆盖Object的clone方法,并将方法作用范围改成public。

clone方法生成规则:

①对任何的对象x,都有x.clone() !=x//克隆对象与原对象不是同一个对象;

②对任何的对象x,都有x.clone().getClass()= =x.getClass()//克隆对象与原对象的类型一样;

③如果对象x的equals()方法定义恰当,那么x.clone().equals(x)应该成立。

实例9:

public class App {
    public static void main(String[] args)  throws Exception{
        
        Person person0 = new Person(new Address("shangHai"),"xiaoHua");
        Person person1=(Person) person0.clone();

        System.out.println(person0==person1);//false
        System.out.println(person1.getName());//xiaoHua
        System.out.println(person1.getAddress().getName());//shangHai
        
        System.out.println(person0.getAddress()==person1.getAddress());//true
        person0.getAddress().setName("beiJing");
        System.out.println(person0.getAddress().getName());//beiJing
        System.out.println(person1.getAddress().getName());//beiJing
    }


}

class Person implements  Cloneable {

    private String name;
    private Address address;

    public Person(Address address, String name) {
        this.address = address;
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    @Override
    public  Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}


class Address{
    private String name;

    public Address(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

上面的例子中可以看出,我们的确的成功地克隆出了person1,但是当我们修改person0的address时,person1的address也发生了改变。person1和person2的address使用的是同一个对象,这种克隆的方式我们称作 浅克隆。有浅克隆当然就会有深克隆。在Java语言里深克隆一个对象,常常可以先使对象实现Serializable接口(如:Person),对象中的属性也要实现Serializable接口(如:Address),然后把对象(实际上只是对象的一个拷贝)写到一个流里,再从流里读出来,便可以重建对象。

public class App {
    public static void main(String[] args) throws Exception {

        Person person0 = new Person(new Address("shangHai"), "xiaoHua");
        Person person1 =(Person) deepClone(person0);

        System.out.println(person0 == person1);//false
        System.out.println(person1.getName());//xiaoHua
        System.out.println(person1.getAddress().getName());//shangHai

        System.out.println(person0.getAddress() == person1.getAddress());//false
        person0.getAddress().setName("beiJing");
        System.out.println(person0.getAddress().getName());//beiJing
        System.out.println(person1.getAddress().getName());//shangHai
    }

    public static Object deepClone(Object o) throws Exception {
        ByteArrayOutputStream bo = null;
        ObjectOutputStream oo = null;
        ByteArrayInputStream bi = null;
        ObjectInputStream oi = null;
        try {
            //将对象写到流里
            bo = new ByteArrayOutputStream();
            oo = new ObjectOutputStream(bo);
            oo.writeObject(o);
            //从流里读出来
            bi = new ByteArrayInputStream(bo.toByteArray());
            oi = new ObjectInputStream(bi);
            return (oi.readObject());
        }finally {
            if (oi != null) {
                oi.close();
            }
            if (bi != null) {
                oi.close();
            }
            if (oo != null) {
                oo.flush();
                oo.close();
            }
            if (bo != null) {
                bo.flush();
                bo.close();
            }
        }
    }
}

class Person implements Cloneable, Serializable {

    private String name;
    private Address address;

    public Person(Address address, String name) {
        this.address = address;
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}


class Address implements Serializable {
    private String name;

    public Address(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}












猜你喜欢

转载自blog.csdn.net/hong10086/article/details/80175109
今日推荐