finalize 和 gc 的區別

finalize()是由JVM自動調用的,你可以用System.gc(),但JVM不一定會立刻執行,JVM感覺內存空間有限時,纔會開始執行finalize(),至於新的對象創建個數和被收集個數不同是因爲收集的對象只和JVM的垃圾收集策略有關。


1.構造函數
要點:
構 建器(Constructor)屬於一種較特殊的方法類型,因爲它沒有返回值.這與 void返回值存在着明顯的區別。對於void返回值,儘管方法本身不會自動返回什麼,但仍然可以讓它返回另一些東西。構建器則不同,它不僅什麼也不會自 動返回,而且根本不能有任何選擇.若創建一個沒有構件器的類,則編譯器會自動創建一個默認構件器.

2.finalize()和gc()

(1)問題:finalize()函數是幹嘛的?Java不是有Garbage Collection(以下簡稱gc)來負責回收內存嗎?
回答:
gc 只能清除在堆上分配的內存(純java語言的所有對象都在堆上使用new分配內存),而不能清除棧上分配的內存(當使用JNI技術時,可能會在棧上分配內 存,例如java調用c程序,而該c程序使用malloc分配內存時).因此,如果某些對象被分配了棧上的內存區域,那gc就管不着了,對這樣的對象進行 內存回收就要靠finalize().
舉個例子來說,當java 調用非java方法時(這種方法可能是c或是c++的),在非java代碼內部也許調用了c的malloc()函數來分配內存,而且除非調用那個了 free() 否則不會釋放內存(因爲free()是c的函數),這個時候要進行釋放內存的工作,gc是不起作用的,因而需要在finalize()內部的一個固有方法 調用它(free()).
finalize的工作原理應該是這樣的:一旦垃圾收集器準備好釋放對象佔用的存儲空間,它首先調用finalize(),而且只有在下一次垃圾收集過程中,纔會真正回收對象的內存.所以如果使用finalize(),就可以在垃圾收集期間進行一些重要的清除或清掃工作.

(2)問題:finalize()在什麼時候被調用?
回答:

有三種情況

1.所有對象被Garbage Collection時自動調用,比如運行System.gc()的時候.
2.程序退出時爲每個對象調用一次finalize方法。
3.顯式的調用finalize方法

除此以外,正常情況下,當某個對象被系統收集爲無用信息的時候,finalize()將被自動調用,但是jvm不保證finalize()一定被調用,也就是說,finalize()的調用是不確定的,這也就是爲什麼sun不提倡使用finalize()的原因.


3. this
要點:
this關鍵字只能在方法中使用,它能爲調用該方法的對象提供相應的句柄,使得同一個類產生的不同對象實例在調用同一方法的時候,系統能判斷出是哪一個對象在進行調用.
比如:
MyObject a=new MyObject();
MyObject b=new MyObject();
a.f();// (3)
b.f();// (4)
編譯器在編譯的時候,實際上是將(3),(4)句解釋爲
MyObject.f(a);
MyObject.f(b);
的,這樣就將調用了該方法的對象的信息傳到了方法中,也就是傳給了this,就可以通過this表示調用該方法的對象實例.

用this的概念還可以解釋爲什麼在靜態方法中不能調用非靜態方法和元素,這是因爲靜態方法中沒有this,也就是說我們不能獲得調用該方法的對象的句柄.既然找不到這個對象實例,我們又怎麼能夠在其中調用對象實例的方法和元素呢?

那 爲什麼靜態方法沒有this呢?用靜態方法的概念可以來理解這個問題.靜態方法是類方法,是所有對象實例公用的方法.它不屬於某一個具體的對象實例,因此 也無法用this來體現這個實例.這和非靜態方法是不一樣的.打個比方,在一個局域網內的幾個用戶每個人都有一臺客戶機,但都訪問一臺公共的服務器.對於 每臺客戶機來說,它的this就是使用它的用戶.而對於服務器來說,它沒有this,因爲它是大家公用的,不針對某一個具體的客戶.

4.對象初始化
要點:
1.對象只有在創建的時候,需要使用它的時候才進行初始化,否則永遠都不會初始化.
2.對象進行初始化是有一定順序的,無論在定義的時候各個成員的擺放位置如何.首先是靜態成員和對象,然後是非靜態成員和對象,最後才運行構造器.
3.靜態成員和對象有且只有一次初始化過程,這個過程發生在第一次創建對象或者第一次使用類的靜態成員和對象的時候.

以一個名爲Dog的類爲例,它的對象實例初始化過程如下:
(1) 類型爲Dog的一個對象首次創建時,或者Dog類的static方法/static字段首次訪問時,Java解釋器必須找到Dog.class(在事先設好的類路徑裏搜索)。
(2) 找到Dog.class後,它的所有static初始化模塊都會運行。因此,static初始化僅發生一次?D?D在Class對象首次載入的時候。
(3) 創建一個new Dog()時,Dog對象的構建進程首先會在內存堆(Heap)裏爲一個Dog對象分配足夠多的存儲空間。
(4) 這種存儲空間會清爲零,將Dog中的所有基本類型設爲它們的默認值
(5) 進行字段定義時發生的所有初始化都會執行。
(6) 執行構建器。正如第6章將要講到的那樣,這實際可能要求進行相當多的操作,特別是在涉及繼承的時候

5.數組的初始化
數組包括基本數據類型數組和對象數組,其中對於對象數組的初始化,經常會出現"Exception"錯誤.比如下面的程序

問題代碼如下:

public userInfo[] getUsersInfo() {

userInfo[] usersInfo=null;

if (users.size()!=0) {
usersInfo=new userInfo[users.size()];

for(int i=0;i< usersInfo.length;i++) {
//+-------------------出問題的地方-----------------
usersInfo[i].name=((User)(users.elementAt(i))).name;
usersInfo[i].type=((User)(users.elementAt(i))).type;
usersInfo[i].userID=((User)(users.elementAt(i))).userID;
//+-------------------出問題的地方-----------------
}
System.out.println("here");
return usersInfo;
}else {
return null;
}
}



其中userInfo的定義爲

class userInfo{
userInfo(String name,int type,int userID){
this.name=name;
this.type=type;
this.userID=userID;
}
String name;
int type;
int userID;
}



運行到程序中標出的問題區域時,系統顯示NullPointerException,爲什麼會這樣呢?

這是因爲,Java在定義數組的時候
usersInfo=new userInfo[users.size()];
並沒有給數組元素分配內存,它只是一個句柄數組,數組中的對象還沒有初始化.因此數組中的每個對象都需要new之後纔可以訪問.例如:
A[] a=new A[2];
for(int i=0;i<2;i++)
a[i] = new A();
這樣才能a[i].someMethod()

因此上面的程序應該改爲

public userInfo[] getUsersInfo() {

userInfo[] usersInfo=null;

if (users.size()!=0) {
usersInfo=new userInfo[users.size()];

for(int i=0;i< usersInfo.length;i++) {
//+-------------------修改的地方-----------------
usersInfo[i]=new userInfo(((User)(users.elementAt(i))).name,
((User)(users.elementAt(i))).type,
((User)(users.elementAt(i))).userID);
}
//+-------------------修改的地方-----------------
return usersInfo;
}else {
return null;
}
}


就 沒問題了簡單來講,finalize()是在對象被GC回收前會調用的方法,而System.gc()強制GC開始回收工作糾正,不是強制,是建議,具體 執行要看GC的意思簡單地說,調用了 System.gc() 之後,java 在內存回收過程中就會調用那些要被回收的對象的 finalize() 方法。

發佈了56 篇原創文章 · 獲贊 8 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章