TIJ讀書筆記(一) (轉)

amyz發表於2007-10-31
TIJ讀書筆記(一) (轉)[@more@]

決定開始啃這本大部頭的寶典,邊啃邊做點筆記吧.廢話少說!

第四章 初始化和清除(Initialization&CleanUp)

主要章節內容:
4.1 構建器
4.2 方法過載
4.3 收尾和垃圾收集
4.4 成員初始化

關鍵詞:Constructor,finalize(),初始化順序

重點整理:

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

2.finalize()和gc()

(1)問題:finalize()函式是幹嘛的?Java不是有Garbage Collection(以下簡稱gc)來負責回收嗎?
回答:
  gc只能清除在堆上分配的記憶體(純java語言的所有都在堆上使用new分配記憶體),而不能清除棧上分配的記憶體(當使用JNI技術時,可能會在棧上分配記憶體,例如javac,而該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關鍵字只能在方法中使用,它能為呼叫該方法的物件提供相應的控制程式碼,使得同一個類產生的不同物件例項在呼叫同一方法的時候,系統能判斷出是哪一個物件在進行呼叫.
比如:
My 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初始化僅發生一次——在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;
}
}
就沒問題了

 


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752019/viewspace-980010/,如需轉載,請註明出處,否則將追究法律責任。

相關文章