一文詳解Java中的finalize()方法
前言
Java的finalize()方法是Object類(lèi)定義的一個(gè)特殊方法,主要用于在對(duì)象被垃圾回收器回收之前執(zhí)行一些清理工作。下面我將從基本概念、工作原理、使用場(chǎng)景、注意事項(xiàng)以及示例代碼等方面詳細(xì)解釋這個(gè)方法。
基本概念
finalize()方法是Java中Object類(lèi)的一個(gè)protected方法,每個(gè)Java類(lèi)都隱式繼承了這個(gè)方法。它的基本語(yǔ)法如下:
protected void finalize() throws Throwable {
// 清理資源的代碼
}
在Object類(lèi)中,finalize()方法的默認(rèn)實(shí)現(xiàn)是空的,即不做任何事情。子類(lèi)可以重寫(xiě)這個(gè)方法來(lái)提供自己的清理邏輯。
工作原理
當(dāng)垃圾回收器(GC)確定一個(gè)對(duì)象不再被任何引用指向時(shí),它會(huì)被標(biāo)記為可回收對(duì)象。在真正回收這個(gè)對(duì)象的內(nèi)存之前,垃圾回收器會(huì)調(diào)用該對(duì)象的finalize()方法。這個(gè)過(guò)程可以簡(jiǎn)單描述為:
- 對(duì)象不再被任何引用指向,成為"不可達(dá)"狀態(tài)
- 垃圾回收器標(biāo)記該對(duì)象為可回收
- 在回收前,垃圾回收器調(diào)用對(duì)象的
finalize()方法 finalize()方法執(zhí)行完畢后,對(duì)象被真正回收
需要強(qiáng)調(diào)的是,finalize()方法的調(diào)用時(shí)機(jī)是不確定的,它取決于垃圾回收器的實(shí)現(xiàn)和調(diào)度策略。垃圾回收器可能在任何時(shí)候決定調(diào)用finalize()方法,也可能永遠(yuǎn)不會(huì)調(diào)用它。
主要用途
1. 資源釋放
這是finalize()方法最常見(jiàn)的用途。當(dāng)對(duì)象持有一些外部資源(如文件句柄、數(shù)據(jù)庫(kù)連接等)時(shí),可以在finalize()方法中釋放這些資源,以避免資源泄漏。
示例:
public class ResourceHolder {
private File file;
public ResourceHolder(String fileName) {
try {
file = new File(fileName); // 打開(kāi)文件進(jìn)行操作
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
protected void finalize() throws Throwable {
try {
if (file != null) {
file.close(); // 在對(duì)象被回收前關(guān)閉文件
}
} finally {
super.finalize();
}
}
}
在這個(gè)例子中,ResourceHolder類(lèi)持有一個(gè)文件對(duì)象。在finalize()方法中,我們檢查文件是否不為空,并在對(duì)象被回收前關(guān)閉文件,以釋放資源。
2. 對(duì)象狀態(tài)重置
可以在finalize()方法中重置對(duì)象的狀態(tài),使其可以被再次使用。這對(duì)于對(duì)象池或緩存對(duì)象等場(chǎng)景非常有用,可以避免頻繁地創(chuàng)建和銷(xiāo)毀對(duì)象,提高程序的性能和效率。
示例:
public class CacheObject {
private int[] cache;
public CacheObject(int size) {
cache = new int[size]; // 初始化緩存數(shù)據(jù)
}
@Override
protected void finalize() throws Throwable {
// 清理緩存數(shù)據(jù)
for (int i = 0; i < cache.length; i++) {
cache[i] = 0;
}
cache = null;
super.finalize();
}
}
在這個(gè)例子中,CacheObject類(lèi)持有一個(gè)整數(shù)數(shù)組作為緩存。在finalize()方法中,我們遍歷緩存數(shù)組并將每個(gè)元素設(shè)置為0,然后將緩存數(shù)組設(shè)置為null,以完成清理工作。
3. 對(duì)象的自我清理
某些對(duì)象在被銷(xiāo)毀之前需要進(jìn)行一些特定的清理操作,例如清理內(nèi)部緩存、重置對(duì)象狀態(tài)等。finalize()方法可以用于實(shí)現(xiàn)這些自我清理邏輯。
示例:
class SelfCleaningObject {
private boolean isCleaned;
public void clean() {
isCleaned = true;
}
@Override
protected void finalize() throws Throwable {
if (!isCleaned) {
// 進(jìn)行自我清理操作
System.out.println("Performing self-cleaning.");
isCleaned = true;
}
super.finalize();
}
}
在這個(gè)例子中,SelfCleaningObject類(lèi)有一個(gè)isCleaned標(biāo)志,用于表示對(duì)象是否已經(jīng)被清理。在finalize()方法中,我們檢查isCleaned標(biāo)志,如果對(duì)象未被清理,則執(zhí)行自我清理操作。
使用示例
下面是一個(gè)更完整的示例,演示了如何在Java中使用finalize()方法進(jìn)行資源清理:
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
public class FinalizeExample {
private FileWriter fileWriter;
public FinalizeExample() {
try {
File file = new File("data.txt");
fileWriter = new FileWriter(file);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
protected void finalize() throws Throwable {
try {
if (fileWriter != null) {
fileWriter.close();
System.out.println("File resources cleaned up in finalize().");
}
} finally {
super.finalize();
}
}
public static void main(String[] args) {
FinalizeExample obj = new FinalizeExample();
System.out.println("Object created with hash code: " + obj.hashCode());
obj = null; // 切斷引用,使對(duì)象成為垃圾
// 建議JVM執(zhí)行垃圾回收
System.gc();
System.out.println("Requested garbage collection.");
// 等待一段時(shí)間,讓垃圾回收有機(jī)會(huì)執(zhí)行
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("End of program.");
}
}
在這個(gè)示例中,我們創(chuàng)建了一個(gè)FinalizeExample對(duì)象,它持有一個(gè)FileWriter實(shí)例。在finalize()方法中,我們關(guān)閉了這個(gè)FileWriter。在main方法中,我們顯式地?cái)嚅_(kāi)對(duì)對(duì)象的引用,并調(diào)用System.gc()建議JVM執(zhí)行垃圾回收。然后我們等待一段時(shí)間,讓垃圾回收有機(jī)會(huì)執(zhí)行。
注意事項(xiàng)
盡管finalize()方法提供了一種在對(duì)象被銷(xiāo)毀之前進(jìn)行清理的機(jī)制,但它存在一些重要的問(wèn)題和限制,需要特別注意:
1. 調(diào)用不確定性
finalize()方法的調(diào)用時(shí)機(jī)是不確定的,它取決于垃圾回收器的實(shí)現(xiàn)和調(diào)度策略。這意味著:
- 不能保證
finalize()方法一定會(huì)被調(diào)用 - 不能保證
finalize()方法何時(shí)被調(diào)用 - 不能保證
finalize()方法的執(zhí)行順序
2. 性能開(kāi)銷(xiāo)
finalize()方法的調(diào)用會(huì)帶來(lái)一定的性能開(kāi)銷(xiāo),因?yàn)樗枰诶厥掌鞯墓ぷ骶€(xiàn)程中執(zhí)行。如果finalize()方法中包含大量的計(jì)算或I/O操作,可能會(huì)影響垃圾回收器的性能,甚至導(dǎo)致系統(tǒng)的性能下降。
3. 異常處理
finalize()方法中可能會(huì)拋出異常,但這些異常會(huì)被忽略,不會(huì)影響垃圾回收器的正常工作。因此,在finalize()方法中應(yīng)該避免拋出異常,或者在拋出異常時(shí)進(jìn)行適當(dāng)?shù)奶幚怼?/p>
4. 對(duì)象復(fù)活
finalize()方法中有一個(gè)特殊的特性:它可以在方法內(nèi)部創(chuàng)建對(duì)該對(duì)象的引用,從而使對(duì)象重新變?yōu)?quot;可達(dá)"狀態(tài)。這被稱(chēng)為"對(duì)象復(fù)活"。這種特性可能導(dǎo)致對(duì)象在垃圾回收后再次存活,從而延長(zhǎng)對(duì)象的壽命,并可能導(dǎo)致一些難以預(yù)測(cè)的問(wèn)題。
示例:
public class FinalizeResurrection {
public static FinalizeResurrection resurrectedObject = null;
@Override
protected void finalize() throws Throwable {
super.finalize();
resurrectedObject = this; // 使對(duì)象復(fù)活
}
public static void main(String[] args) throws InterruptedException {
FinalizeResurrection obj = new FinalizeResurrection();
obj = null; // 切斷引用
System.gc(); // 建議垃圾回收
// 等待finalize()方法執(zhí)行
Thread.sleep(1000);
if (resurrectedObject != null) {
System.out.println("Object has been resurrected!");
} else {
System.out.println("Object has not been resurrected.");
}
resurrectedObject = null; // 再次切斷引用
System.gc(); // 再次建議垃圾回收
Thread.sleep(1000);
if (resurrectedObject == null) {
System.out.println("Object is finally garbage collected.");
}
}
}
在這個(gè)例子中,FinalizeResurrection類(lèi)在finalize()方法中將自己賦值給一個(gè)靜態(tài)變量,從而使對(duì)象"復(fù)活"。第一次垃圾回收后,對(duì)象會(huì)復(fù)活;第二次垃圾回收后,對(duì)象才會(huì)真正被回收。
5. 并發(fā)問(wèn)題
finalize()方法是在垃圾回收器線(xiàn)程中調(diào)用的,而不是在創(chuàng)建對(duì)象的線(xiàn)程中調(diào)用。這可能導(dǎo)致一些并發(fā)問(wèn)題,特別是在對(duì)象狀態(tài)依賴(lài)于創(chuàng)建線(xiàn)程上下文的情況下。
6. 內(nèi)存泄漏風(fēng)險(xiǎn)
由于finalize()方法的調(diào)用是不確定的,過(guò)度依賴(lài)finalize()方法可能會(huì)導(dǎo)致內(nèi)存泄漏。例如,如果在finalize()方法中創(chuàng)建了新的對(duì)象,并且這些對(duì)象沒(méi)有被正確引用,那么它們將不會(huì)被垃圾回收,從而導(dǎo)致內(nèi)存泄漏。
示例:
public class MemoryLeakExample {
private static final List<MemoryLeakExample> instances = new ArrayList<>();
public MemoryLeakExample() {
instances.add(this);
}
@Override
protected void finalize() throws Throwable {
instances.remove(this);
}
}
在這個(gè)例子中,MemoryLeakExample類(lèi)的每個(gè)實(shí)例都會(huì)被添加到一個(gè)靜態(tài)列表中。在finalize()方法中,我們從列表中刪除了當(dāng)前對(duì)象。然而,由于finalize()方法的調(diào)用是不確定的,可能會(huì)導(dǎo)致一些對(duì)象無(wú)法被正確刪除,從而導(dǎo)致內(nèi)存泄漏。
替代方案
由于finalize()方法存在上述問(wèn)題和限制,Java社區(qū)推薦使用其他機(jī)制來(lái)管理資源釋放和對(duì)象清理。以下是一些更好的替代方案:
1. try-with-resources語(yǔ)句
Java 7及更高版本引入了try-with-resources語(yǔ)句,它可以自動(dòng)關(guān)閉實(shí)現(xiàn)了AutoCloseable接口的資源,而不需要顯式調(diào)用finalize()方法。
示例:
public class TryWithResourcesExample {
public static void main(String[] args) {
try (FileWriter fileWriter = new FileWriter("data.txt")) {
fileWriter.write("Hello, World!");
} catch (IOException e) {
e.printStackTrace();
}
// fileWriter會(huì)自動(dòng)關(guān)閉,無(wú)需在finalize()中處理
}
}
在這個(gè)例子中,FileWriter實(shí)現(xiàn)了AutoCloseable接口,因此在try-with-resources塊結(jié)束時(shí),它會(huì)自動(dòng)被關(guān)閉,無(wú)需依賴(lài)finalize()方法。
2. 顯式資源釋放
對(duì)于不適用try-with-resources的資源,可以在代碼中顯式地釋放資源,通常使用try-finally塊來(lái)確保資源被釋放。
示例:
public class ExplicitResourceRelease {
public static void main(String[] args) {
FileWriter fileWriter = null;
try {
fileWriter = new FileWriter("data.txt");
fileWriter.write("Hello, World!");
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fileWriter != null) {
try {
fileWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
在這個(gè)例子中,我們使用try-finally塊確保FileWriter被正確關(guān)閉,而不依賴(lài)?yán)厥諜C(jī)制。
3. 對(duì)象池模式
對(duì)于需要重用的對(duì)象,可以使用對(duì)象池模式,而不是依賴(lài)finalize()方法進(jìn)行對(duì)象狀態(tài)重置。
示例:
public class ObjectPoolExample {
private static final List<PoolableObject> pool = new ArrayList<>();
public static PoolableObject acquire() {
if (!pool.isEmpty()) {
return pool.remove(0);
}
return new PoolableObject();
}
public static void release(PoolableObject obj) {
obj.reset();
pool.add(obj);
}
}
class PoolableObject {
// 對(duì)象狀態(tài)
private String state;
public void use(String newState) {
this.state = newState;
}
public void reset() {
this.state = null;
}
}
在這個(gè)例子中,我們創(chuàng)建了一個(gè)對(duì)象池,對(duì)象在使用后會(huì)被重置并放回池中,而不是依賴(lài)?yán)厥諜C(jī)制。
現(xiàn)代Java中的地位
隨著Java語(yǔ)言的發(fā)展,finalize()方法的重要性已經(jīng)大大降低。Java 9中引入了java.lang.ref.Cleaner類(lèi)作為finalize()方法的替代方案,它提供了更可靠和更可控的資源清理機(jī)制。Java 14中甚至計(jì)劃移除finalize()方法,因?yàn)樗徽J(rèn)為是Java語(yǔ)言中的一個(gè)設(shè)計(jì)缺陷。
現(xiàn)代Java編程中,除非有特殊需求,否則不建議使用finalize()方法。更好的做法是:
- 使用try-with-resources語(yǔ)句管理可關(guān)閉資源
- 使用try-finally塊確保資源被釋放
- 對(duì)于對(duì)象池等場(chǎng)景,使用顯式的對(duì)象重置方法
附:java里final、finally、finalize的區(qū)別
final :java 關(guān)鍵字。被final修飾的變量不可進(jìn)行值更改,必須在定義時(shí)一并初始化。如final int i=1,則下面對(duì)i只能使用,而不能進(jìn)行更改如i++,更改必定會(huì)報(bào)錯(cuò)。同理,final修飾方法時(shí),則子類(lèi)不能對(duì)該方法進(jìn)行重寫(xiě);被final修飾的類(lèi)不允許繼承。所以,一個(gè)類(lèi)不能不同被abstract和final修飾。(實(shí)操驗(yàn)證見(jiàn)下圖)

finally:多見(jiàn)與java的try..catch..finally塊,只有try語(yǔ)句塊正常執(zhí)行(不做退出線(xiàn)程,死機(jī)等情況的考慮),那么無(wú)論catch語(yǔ)句塊是否執(zhí)行 ,finally語(yǔ)句塊都會(huì)執(zhí)行,且最終整個(gè)try..finally代碼塊返回情況以finally中的return結(jié)果為準(zhǔn)(見(jiàn)下圖)。所以在代碼邏輯中有需要無(wú)論發(fā)生什么都必須執(zhí)行的代碼,就可以放在finally塊中。

finalize:類(lèi)的finalize()方法,可以告訴垃圾回收器應(yīng)該執(zhí)行的操作,該方法從Object類(lèi)繼承而來(lái)。在從堆中永久刪除對(duì)象之前,垃圾回收器調(diào)用該對(duì)象的finalize()方法。下圖是Object類(lèi)中的finalize 方法。

總結(jié)
Java的finalize()方法是一個(gè)特殊的方法,它在對(duì)象被垃圾回收器回收之前被調(diào)用,主要用于執(zhí)行一些清理工作。雖然它提供了一種在對(duì)象銷(xiāo)毀前進(jìn)行資源釋放和狀態(tài)重置的機(jī)制,但由于其調(diào)用不確定性、性能開(kāi)銷(xiāo)和潛在的并發(fā)問(wèn)題,現(xiàn)代Java編程中已經(jīng)不推薦使用它。
更好的做法是使用try-with-resources語(yǔ)句、try-finally塊和顯式的資源管理代碼來(lái)確保資源被正確釋放。對(duì)于對(duì)象池等場(chǎng)景,應(yīng)該使用顯式的對(duì)象重置方法,而不是依賴(lài)finalize()方法。隨著Java語(yǔ)言的發(fā)展,finalize()方法的重要性已經(jīng)大大降低,未來(lái)可能會(huì)被完全移除。
到此這篇關(guān)于Java中finalize()方法的文章就介紹到這了,更多相關(guān)Java中finalize()方法內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring Boot 緩存優(yōu)化Redis 實(shí)戰(zhàn)指南
本文介紹了如何在SpringBoot項(xiàng)目中集成Redis緩存,以?xún)?yōu)化性能并提高用戶(hù)體驗(yàn),通過(guò)實(shí)戰(zhàn),讀者可以快速掌握Redis緩存的集成方法,并在實(shí)際項(xiàng)目中應(yīng)用,解決數(shù)據(jù)量大的性能瓶頸,感興趣的朋友跟隨小編一起看看吧2026-03-03
JAVA8 List<List<Integer>> list中再裝一個(gè)list轉(zhuǎn)成一個(gè)list操
這篇文章主要介紹了JAVA8 List<List<Integer>> list中再裝一個(gè)list轉(zhuǎn)成一個(gè)list操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-08-08
SpringCloud應(yīng)用idea實(shí)現(xiàn)可相互調(diào)用的多模塊程序詳解
IDEA 全稱(chēng) IntelliJ IDEA,是java編程語(yǔ)言的集成開(kāi)發(fā)環(huán)境。IntelliJ在業(yè)界被公認(rèn)為最好的Java開(kāi)發(fā)工具,尤其在智能代碼助手、代碼自動(dòng)提示、重構(gòu)、JavaEE支持、各類(lèi)版本工具(git、svn等)、JUnit、CVS整合、代碼分析、 創(chuàng)新的GUI設(shè)計(jì)等方面的功能可以說(shuō)是超常的2022-07-07
SpringBoot整合Redis實(shí)現(xiàn)訂單超時(shí)自動(dòng)刪除功能
在電商、外賣(mài)等場(chǎng)景中,訂單超時(shí)未支付是常見(jiàn)業(yè)務(wù)場(chǎng)景,傳統(tǒng)方案通過(guò)定時(shí)任務(wù)輪詢(xún)數(shù)據(jù)庫(kù),但存在??延遲高??、??數(shù)據(jù)庫(kù)壓力大等問(wèn)題,Redis的??過(guò)期鍵自動(dòng)刪除機(jī)制??,可完美解決這一痛點(diǎn),所以本文給大家介紹了SpringBoot整合Redis實(shí)現(xiàn)訂單超時(shí)自動(dòng)刪除2025-07-07
SpringMvc3+extjs4實(shí)現(xiàn)上傳與下載功能
這篇文章主要為大家詳細(xì)介紹了SpringMvc3+extjs4實(shí)現(xiàn)上傳與下載功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-06-06
JAVA包裝類(lèi)及自動(dòng)封包解包實(shí)例代碼
JAVA包裝類(lèi)及自動(dòng)封包解包實(shí)例代碼,需要的朋友可以參考一下2013-03-03
關(guān)于自定義過(guò)濾器獲取不到session問(wèn)題
這篇文章主要介紹了關(guān)于自定義過(guò)濾器獲取不到session問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01

