垃圾回收器(garbage colector)决定回收某对象时,就会运行该对象的 finalize() 方法 但是在 Java 中很不幸,如果内存总是充足的,那么垃圾回收可能永远不会进行,也就是说 filalize() 可能永远不被执行,显然指望它做收尾工作是靠不住的。 那么finalize() 究竟是做什么的呢? 它最主要的用途是回收特殊渠道申请的内存。Java 程序有垃圾回收器,所以一般情况下内存问题不用程序员操心。但有一种 JNI(Java Native Interface)调用non-Java 程序(C 或 C++), finalize() 的工作就是回收这部分的内存。
finalize() 方法
Java 可以使用 finalize() 方法在垃圾收集器将对象从内存中清除出去之前做一些必要的清理工作。
finalize()方法什么时候被调用?
这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的。
析构函数(finalization) 的目的是什么?
析构函数的目的是:在清除对象前,完成一些清理工作,比如:释放内存等。
final 和 finalize 的区别?
final关键字可以用于类、方法、变量前,用来表示该类、方法、变量具有不可变的特性。 finalize方法用于回收资源,可以为任何一个类添加finalize方法。该方法将在垃圾回收器清除对象之前调用。
与toString方法类似,finalize方法也是Java中所有类中必有的方法,因为它是属于Object根类的方法,默认情况下,Java中所有的类都是Object类的子类。
finalize方法会被自动调用 ,调用的时机 为:在销毁(回收)该对象前,finalize方法会被自动调用;
以上这句话听起来的确很抽象,什么是销毁(回收)对象呢?所谓的销毁对象就是把该对象所占用的堆内存空间给释放掉,也就是释放出更多的内存资源,释放出来的内存空间可以供别的对象使用, 这一点是非常重要的**。**
搞清楚这个问题之后,我们就要思考,什么时候会销毁对象呢?**当垃圾回收器确定不存在该对象的更多引用时,由垃圾回收器自动调用该方法。**我画一个图解释一下,比如说我现在有一个Dog对象,它在堆内存中占了一片空间,此时d是这个对象的引用。如下图所示
当我调用完Dog对象的eat方法之后,以后就再也不使用这个Dog对象了,可以将d赋值为null,那么原来那个Dog对象就没有被引用了,将来也不会再用到。此时,垃圾回收器会自动回收或者说销毁该对象,那是不是一把d赋值为null,就马上销毁该对象呢?不是的,垃圾回收器到底什么时候回收该对象,有它自己的规则,并不是说该对象没被引用了,马上回收该对象,我们也可以来进行验证一下,根据上文的描述,我们知道,当该对象被回收时,会先去调用finallize方法,默认情况下,子类的finalize方法是继承自Object父类,先来看看Object父类中的finalize方法是如何编写的,如下图所示:
根据观察可以发现,Object类的finalize方法中没有任何的代码。为了方便观察到效果,我们在Dog类中重写父类Object的finalize方法,如下图所示:
重写完之后,我们运行程序,来验证finalize方法在此时是否会被自动调用,此时main方法中的代码是这样的,在第7行,已经将d赋值为null,意味着Dog对象已经没有任何引用了。
运行程序,结果如下所示:观察发现,明明Dog对象已经没有被引用了,为什么finalize没有被调用呢?如我刚刚所说,虽然Dog对象没有被引用,但是垃圾回收器并不会马上回收Dog对象,它有自己的回收时机,现在只能确定该Dog对象已经可以被回收了,但是还没被回收,所以finalize方法没有被调用。
为了看到显示的效果,我们可以编写代码,手动调用垃圾回收器让它回收该Dog对象,在main方法中,我们添加上这样一句代码,调用垃圾回收器:
再次运行程序,观察结果:发现finalize方法果然被自动调用了,但是你再仔细观察代码发现,代码的输出是先执行第9行,然后再去调用finalize方法,也就是说System.gc调用之后,并不会阻塞在那里,等调用完finalize方法之后再执行第9行,而是一调用垃圾回收器就继续运行代码了,就先输出了第9行的代码,finalize方法反而在其后,这是允许的。
在实际的编程开发中,finalize方法一般是用来释放资源 (比如数据库的连接对象 ,文件流的对象 等这些都叫资源)的,且作为程序员,我们很少在子类中去重写这个方法。