JAVA-Object
Object类是所有类的基类,了解他很是重要
分类来看Object类的方法
- hashCode()和equals(Object obj)
- clone()
- notify()、notifyAll()、wait(long timeout)、wait(long timeout, int nanos)、wait()
- finalize()
- toString()
hashCode & equals
hashCode()方法由native方法底层实现,默认由对象的地址转换而来,对底层是散列表的对象有提升性能的功能。
- 同一个对象(如果该对象没有被修改):那么重复调用hashCode()那么返回的int是相同的
equals()方法默认是比较对象的地址,使用的是==等值运算符
- 传入的参数为null,返回的是false
二者关系
- 重写equals()方法,就必须重写hashCode()的方法
- 同一个对象(如果该对象没有被修改):那么重复调用hashCode()那么返回相同、重复调用equals返回相同
todo: hashCode和HashMap的key的关系:看一下美团技术团队的文章
todo: 如何编写hashCode和equals方法
要注意这两个方法在集合、容器上的作用,可以看另一篇介绍集合和容器的文章;另外这两个方法对于一些compare接口的实现可能也有着作用效果,值得注意
clone
todo: 值得注意的是深拷贝和浅拷贝问题
克隆的对象要实现Cloneable接口(该接口没有任何方法),重写clone方法。
Address address = new Address(1, 2, 3);
address.clone();//error
上面的代码不能执行成功,因为clone在Object中是protected方法,而Address和Object不在同一包之下
wait & notify
调用这些方法必须持有锁,即必须在synchronized中调用(todo: ???可不可以被lock包围???),否则会抛出异常。
调用wait()的线程会释放掉锁。导致wait()的线程被唤醒可以有4种情况
-
该线程被中断
-
wait()时间到了
-
被notify()唤醒
-
被notifyAll()唤醒
notify()唤醒的是在等待队列的某个线程(不确定会唤醒哪个),notifyAll()唤醒的是等待队列所有线程。notifyAll()唤醒所有进程后,哪个线程接下来能够获取到锁要具体依赖于操作系统的调度。被唤醒之后不意味着立刻就能获得到锁。
- notify方法调用后,被唤醒的线程不会立马获得到锁对象。而是等待notify的同步代码块执行完之后才会获得锁对象
- Thread.sleep()与Object.wait()二者都可以暂停当前线程,释放CPU控制权。主要的区别在于锁释放上面:Object.wait()在释放CPU同时,释放了对象锁的控制。而Thread.sleep()没有对锁释放
- wait和notify方法均可释放对象的锁,但wait同时释放CPU控制权,即它后面的代码停止执行,线程进入阻塞状态,而notify方法不立刻释放CPU控制权,而是在相应的synchronized(){}语句块执行结束,再自动释放锁
要注意这些方法在并发上的作用,可以看另一篇介绍并发库文章,值得注意
finalize
finalize()在垃圾回收器清除对象之前调用,一般不会重写。如非必要,尽量避免使用finalize。finalize不应该用来管理socket, file handle, database connection等昂贵资源,因为finalize是在object被GC的时候才调用——如果内存足够,它可能永远都不会被调用。
对于关闭JVM的时候必须要做的事情,应该用Runtime#addShutdownHook(Thread hook)来实现,放到finalize里面的code并不保证在这个时候会被调用
即使Override了finalize,事情也没有那么简单,首先,override了finalize的object并不是和其他情况下一样直接释放内存,也不是直接调用finalize,而是放到finalizing queue,由JVM上一个单独的thread来进行处理,同时你要当心在finalize里面不小心调用了code,把object自己重新reference到——这个时候它就复活了,可能事情做到一半,又从finalizing queue里面拿出来了,造成不稳定的状态——这种风险是不值得绝大多数情况下的应用场景的。
Java的垃圾回收器只会释放由我们new出来的内存堆块,那些不是由new出来的“特殊内存”,垃圾回收器是不会管理的。这些所谓的特殊内存指通过JNI用C/C++向系统申请的内存,这些内存如果不手动去清除就会一直占据在内存中。
finalize在对象被垃圾收集器析构(回收)之前调用,finalize()方法由GC至多执行一次(用户当然可以手动调用对象的finalize()方法,但并不影响GC对finalize()的行为)。
执行流程:当对象变成(GC Roots)不可达时,GC会判断该对象是否覆盖了finalize方法,若未覆盖,则直接将其回收。否则,若对象未执行过finalize方法,将其放入F-Queue队列,由一低优先级线程执行该队列中对象的finalize方法。执行finalize方法完毕后,GC会再次判断该对象是否可达,若不可达,则进行回收,否则,对象“复活”。
finalize清理可以用于:
- 清理本地对象(通过JNI创建的对象);
- 确保某些非内存资源(如Socket、数据库、文件等)的释放:在finalize()方法中显式调用其他资源释放方法。
需要注意执行 super.finalize();