Java Object对象

Object.getClass()和Object.class区别

  • 类名.class叫做“类字面量”,因class是关键字, 所以类名.class编译时确定。而getclass()是某个具体的方法来调用,是运行时根据实际实例确定,getClass()是动态而且是final的

HashCode

  • ava中的hashCode方法就是根据一定的规则将与对象相关的信息(比如对象的存储地址,对象的字段等)映射成一个数值,这个数值称作为散列值
  • Hashcode在集合中添加新的元素提高很多效率。如果集合中已经存在一万条数据或者更多的数据,如果采用equals方法去逐一比较,效率必然是一个问题。此时hashCode方法的作用就体现出来了,当集合要添加新的对象时,先调用这个对象的hashCode方法,得到对应的hashcode值,实际上在HashMap的具体实现中会用一个table保存已经存进去的对象的hashcode值,如果table中没有该hashcode值,它就可以直接存进去,不用再进行任何比较了;如果存在该hashcode值, 就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址
  • HashMap 添加元素实现
public V put(K key, V value) {
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key.hashCode());
        int i = indexFor(hash, table.length);
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
 
        modCount++;
        addEntry(hash, key, value, i);
        return null;
    }

可以直接根据hashcode值判断两个对象是否相等吗?

肯定是不可以的,因为不同的对象可能会生成相同的hashcode值。如果两个对象的hashcode值不等,则必定是两个不同的对象,hashcode相等,两个对象不一定相等,如果要判断两个对象是否真正相等,必须通过equals方法。

  • 两个对象,如果调用equals方法得到的结果为true,则两个对象的hashcode值必定相等
  • 如果equals方法得到的结果为false,则两个对象的hashcode值不一定不同
  • 如果两个对象的hashcode值不等,则equals方法得到的结果必定为false
  • 如果两个对象的hashcode值相等,则equals方法得到的结果未知

Clone方法

protected native Object clone() throws CloneNotSupportedException;
  • 对于任何对象 x,表达式 x.clone() != x 为true,x.clone().getClass() == x.getClass() 也为true
  • Cloneable和Serializable一样都是标记型接口,它们内部都没有方法和属性,implements Cloneable表示该对象能被克隆,能使用Object.clone()方法.由于Object本身没有实现Cloneable接口,所以不重写clone方法并且进行调用的话会发生CloneNotSupportedException异常
  • Clone 要注意深拷贝还是浅拷贝
    (1)浅克隆(shallow clone),浅拷贝是指拷贝对象时仅仅拷贝对象本身和对象中的基本变量,而不拷贝对象包含的引用指向的对象。
    (2)深克隆(deep clone),深拷贝不仅拷贝对象本身,而且拷贝对象包含的引用指向的所有对象。

wait/notify/notifyAll

Java可以使用wait/notify/notifyAll实现线程间的通信,在了解wait 和 notify 之前,我们先了解下Java对象的监视器(monitor

Monitor–对象监视器

  • JVM 给每个对象和Class字节码都设置了一个监视器Monitor,用于检测并发代码的重入,同时还在 Object类中提供了notify和wait方法来对线程进行控制
  • 在这里插入图片描述
  1. Monitor可以类比一个房间,他保证每次只有一个线程能进入到这个房间里访问被保护的数据
  2. 当线程需要访问被保护的数据时,排队在一个叫Entry Set的队列中,等待占用这个Monitor的线程释放Monitor后去竞争获取这个监视器
  3. 当线程释放了Monitor后就进入了Wait Set队列,当对象调用了notify之后,Wait Set队列中的线程就会被再次唤醒,只不过也许要竞争,只有一个线程才能获得监视器
  4. 总之Monitor是对于Object而言的,任何情况下,都只有一个线程持有该对象的监视器

Monitor实现机制

有如下代码

public class Test {
    public static void main(String[] args) {
        Object object = new Object();
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    object.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}

在另一个线程中去调用wait方法,会抛出如下异常

Exception in thread "Thread-0" java.lang.IllegalMonitorStateException
	at java.lang.Object.wait(Native Method)
	at java.lang.Object.wait(Unknown Source)
	at Test$1.run(Test.java:9)
	at java.lang.Thread.run(Unknown Source)

这是因为我们在主线程中创建了obj对象,在子线程中调用了wait方法,但是obj实际控制权还在主线程中

如何获取对象的监视器

在Java中,我们使用synchronized实现线程同步来获取对象的监视器

synchronized(Obejct obj) {
    //同步代码块
    ...
}
public synchronized void set(int val) {
    this.val = val;
}
public static synchronized void set(Test instance) {
    Test.instance = instance;
}
  1. synchronized修饰的obj是类.class时,标识获取该类字节码的监视器,但是对于访问对象的实例方法并不受影响
  2. synchronized修饰的是非静态方法时,实际上获取的是this对象的监视器,其他需要获取该对象打的监视器会受到阻塞
  3. synchronized修饰的是静态方法时,因为static方法不属于任何对象,而是属于类的方法,获取的是类字节码的监视器,同1

线程间通信方法

线程间的通信可以使用wait、notify、notifyAll来进行控制,wait()来让一个线程在某些条件下暂停运行,notify去通知一个线程运行,而notifyAll会唤醒所有等待中的线程。

notify和notifyAll

  • 前者只能唤醒一个正在等待这个对象的monitor的线程,具体由JVM决定,后者则会唤醒所有正在等待这个对象的monitor的线程,但是要注意:
  1. 调用notify方法,并不意味着释放了Monitor,必须要等同步代码块结束后才会释放Monitor
  2. 多线程之间如果wait先于notify执行,或者是notify时没有wait的线程,那么notify不会有任何唤醒效果,或导致其他线程

wait

  1. wait()方法执行时,会释放锁,同时线程挂起
  2. 永远在循环(loop)里调用 wait 和 notify,不是在 If 语句
  3. wait()/notify()/notifyAll() 方法在使用时, 必须使用同一个对象的锁。

finalize方法

类似于C++的析构函数,当类对象的内存不再使用时,GC会调用这个方法,该方法中不要激活对象的引用,不然会造成假死状态,导致GC不会立马回收该对象

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章