JAVA-Object

Object类是所有类的基类,了解他很是重要

分类来看Object类的方法

  1. hashCode()和equals(Object obj)
  2. clone()
  3. notify()、notifyAll()、wait(long timeout)、wait(long timeout, int nanos)、wait()
  4. finalize()
  5. 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清理可以用于:

  1. 清理本地对象(通过JNI创建的对象);
  2. 确保某些非内存资源(如Socket、数据库、文件等)的释放:在finalize()方法中显式调用其他资源释放方法。

需要注意执行 super.finalize();

posted @ 2020-03-23 00:37  cheaptalk肥皂  阅读(114)  评论(0编辑  收藏  举报