Object类方法分析

1. == 和 equals()的区别

	public boolean equals(Object obj) {
        return (this == obj);
    }

Object源码中equals方法和"==“并没有区别,它就是用” ==" 写的。
可以知晓

  1. 如果是基本数据类型,也就是byte,int,long, double之类的数据类型 "== "是比较数值的
  2. 如果是对象,"=="是比较对象的地址是否相同,其实就是比较是否是同一个对象。

但是float,double这两种浮点类型,如果用来比较大小的话,可能会出现精度丢失的问题。

		float a = 1.0f - 0.9f;
        float b = 0.9f - 0.8f;
        if (a == b) {
            System.out.println("true");
        } else {
            System.out.println("false");
        }

这一段代码的输出结果是false,因为出现了精度丢失
即使是他们的包装类型Float以及Double也一样会出现精度丢失

		Float a = Float.valueOf(1.0f - 0.9f);
        Float b = Float.valueOf(0.9f - 0.8f);
        if (a.equals(b)) {
            System.out.println("true");
        } else {
            System.out.println("false");
        }

输出结果也是false,所以在阿里巴巴编码规约里规定,浮点型的基本数据类型不能用"=="比较,包装数据类型不能用equals()比较。

hashCode() 和 equals()

public native int hashCode();

这是一个native方法,返回的是一个int。这个方法是用来获取哈希码的,就是对象在堆中的一个特殊值。

类似于HashMap中的hash。

	static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

通过传进来的对象,生成一个独特的hash值,来代表它存储在HashMap底层的数组下标。

所以如果需要判断对象是否相等,就一定需要覆盖hashCode()方法以及equals()方法。

  1. 首先进行判断它们的hashCode 是否相等,如果不相等,那么这两个对象就一定不相等。
  2. 如果这两个对象的hashCode相等,那么就会使用equals()方法去判断这两个对象是否真的相等。

关于hashCode以及equals的一些规则

  1. 如果两个对象相等,则hashcode一定也是相同的
  2. 两个对象相等,对两个对象分别调用equals方法都返回true
  3. 两个对象有相同的hashcode值,它们也不一定是相等的

3. toString()

 	public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

返回当前对象的类名,以及使用16进制转换完后的hashCode值

这个toString()方法于直接使用System.out.println()输出对象是一样的结果

		Object object = new Object();
        System.out.println(object);
        System.out.println(object.toString());

输出结果为

		java.lang.Object@1540e19d
		java.lang.Object@1540e19d

4. clone()

如何实现对象克隆?

  1. 实现cloneable接口
  2. 重写Object的clone()方法
直接赋值
		Object o1 = new Object();
		Object o2 = o1;

在Java中,这种直接赋值方式,就是赋值引用,也就是说它们指向的是同一个对象

需要注意的是一个类覆盖clone方法需要实现Cloneable接口,不然会抛出CloneNotSupportedException异常

浅拷贝

拷贝但不拷贝引用的对象,即创建一个对象,如果对象中的是值类型,则进行拷贝,如果是引用类型,则拷贝对象的引用但不拷贝引用的对象。

public class FatherClass implements Cloneable {
    private String name;
    private int age;
    private ChildClass child;

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

    public static void main(String[] args) throws CloneNotSupportedException {
        FatherClass father = new FatherClass();
        father.name = "张三";
        father.age = 30;
        father.child = new ChildClass();
        father.child.setName("小张三");
        father.child.setAge(20);

        FatherClass father1 = (FatherClass) father.clone();
        //false
        System.out.println(father == father1);
        //356573597
        System.out.println(father.hashCode());
        //1735600054
        System.out.println(father1.hashCode());
        //张三
        System.out.println(father.name);
        //张三
        System.out.println(father1.name);
        //true
        System.out.println(father.child == father1.child);
        //21685669
        System.out.println(father1.child.hashCode());
        //21685669
        System.out.println(father.child.hashCode());
    }
}

这是一个浅拷贝的例子,它们输出的结果在语句的上方,方便查看。
浅拷贝说明,clone()方法确实创建了一个对象,因为它们的地址以及hashCode均不相同。
而且,它们的值类型都相等,引用类型childClass 也都指向同一个引用,因为它们相等,并且hashCode也相等。

深拷贝

深拷贝不仅复制对象本身,而且复制对象包含的引用指向的所有对象。

如何实现一个深拷贝?

  1. 序列化这个对象,再序列化回来
  2. 修改clone()方法的内容,将引用类型也使用clone()

首先将ChildClass也覆盖clone方法

public class ChildClass implements Cloneable {

    private String name;

    private int age;

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }


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

接着修改FatherClass里的clone方法

@Override
    protected Object clone() throws CloneNotSupportedException {
        FatherClass fatherClass = (FatherClass) super.clone();
        fatherClass.child = (ChildClass) this.child.clone();
        return fatherClass;
    }
public static void main(String[] args) throws CloneNotSupportedException {
        FatherClass father = new FatherClass();
        father.name = "张三";
        father.age = 30;
        father.child = new ChildClass();
        father.child.setName("小张三");
        father.child.setAge(20);

        FatherClass father1 = (FatherClass) father.clone();
        //false
        System.out.println(father == father1);
        //356573597
        System.out.println(father.hashCode());
        //1735600054
        System.out.println(father1.hashCode());
        //张三
        System.out.println(father.name);
        //张三
        System.out.println(father1.name);
        //false
        System.out.println(father.child == father1.child);
        //21685669
        System.out.println(father1.child.hashCode());
        //2133927002
        System.out.println(father.child.hashCode());
        }

可以看到child比较也是false,并且hashCode也不相同,说明引用类型指向的也不是同一个引用了。

深拷贝和浅拷贝也是相对的,如果一个类中只有基本数据类型,那么执行clone方法就是深拷贝,如果还包括引用数据类型,那么就是浅拷贝了。

5. notify()和notifyAll()

这两个方法都是用来唤醒线程的,只不过notify()是随机唤醒一个线程,notifyAll()是唤醒所有的线程。

一般来说notify(), notifyAll()都是基于wait()方法以及synchronized 关键字而言。
因为notify(),notifyAll() 是基于wait()方法,是用来唤醒等待中得线程得,而wait()方法又是基于synchronized关键字,只能在synchronized方法或块中使用,因为只有获得了锁,才能释放锁。

wait()方法又引出锁池和等待池的概念

锁,锁池,等待池都是对于对象而言,synchronized也是锁住对象而不是锁住代码。

锁池:当线程A已经获得了某个对象的锁得拥有权,而线程B想要进入这个对象得synchronized方法或块得时候,就需要获取到这个对象的锁得拥有权,但是因为线程A已经获得了,所以线程B就会被阻塞,进入到一个地方去等待线程A释放锁,这个地方就是锁池。

等待池:线程A调用了某个对象得wait()方法,那么线程A就会使用这个对象得锁,同时线程A就进入到了这个对象得等待池中了,进入等待池中得线程不会去竞争锁。

notify(),notifyAll()就是在等待池中唤醒线程进入到锁池中去竞争锁。不同的是前者是随机唤醒一个,而后者是全部唤醒。

public static void main(String[] args) {

        final Object lock = new Object();
        new Thread(new Runnable() {
            public void run() {
                System.out.println("thread A is waiting to get lock");
                synchronized (lock) {
                    try {
                        System.out.println("thread A get lock");
                        Thread.sleep(20);
                        System.out.println("thread A do wait method");
                        //不填参数会进入无限期的等待
                        lock.wait();
                        System.out.println("thread A is done");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();

        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(new Runnable() {
            public void run() {
                System.out.println("thread B is waiting to get lock");
                synchronized (lock) {
                    try {
                        System.out.println("thread B get lock");
                        System.out.println("thread B is sleeping 10ms");
                        Thread.sleep(10);
                        System.out.println("thread B is done");
                        //能够唤醒线程
                        lock.notify();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }

运行结果如下:

thread A is waiting to get lock
thread A get lock
thread B is waiting to get lock
thread A do wait method
thread B get lock
thread B is sleeping 10ms
thread B is done
thread A is done

结合代码分析:
①线程A在获取到了锁之后
②线程B被阻塞,进入lock对象的锁池当中,
③接着线程A调用lock对象的wait方法,进入lock对象的等待池中,
④直到线程B中调用lock的notify()方法唤醒,
⑤然后线程A才能输出 “thread A is done” 这个语句。

参考资料:
JavaGuide
细说 Java 的深拷贝和浅拷贝
慕课网----剑指Offer直通车

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值