《Java編程思想》第四版讀書筆記 第五章

5.1程序員

練習2中提到比較兩個字符串域在定義就初始化和經過構造函數初始化的區別:編程

兩個域都是在構造函數調用前就被初始化,不一樣的是一個給出了初始值,一個Java編譯器賦給默認值null。而後經過構造函數再一遍複製。經過構造函數複製更靈活一些。數組

 

5.2安全

函數重載與基本類型提高相結合:函數

基本類型能從一個較小的類型自動提高至一個較大的類型,這種狀況牽扯函數重載易形成混淆。測試

整數類型的字面常量都被當成int型參數。其餘狀況下,傳入的數據類型(實參)小於方法中聲明的形參類型,實參的數據類型就會被提高。char類型比較特殊,若是沒法找到接受char參數的方法,就會把char直接提高成int型。this

若是傳入的實參類型小於重載方法聲明的形參則必須強制轉換成形參的類型,不然編譯器會報錯。lua

 

5.4spa

this關鍵字 只能在方法內部使用,表示對調用方法的那個對象的引用。本書做者不推薦在沒有必要的時候使用this關鍵字。翻譯

 

能夠在構造函數中使用this和參數列表,調用相關的構造函數。但在構造函數中僅能夠調用一次,而且必須在構造函數的最起始處。除構造函數外,編譯器禁止在其餘任何方法中調用構造函數。

注意構造函數中不能經過構造函數的名字調用其餘重載的構造函數。

 

5.5

垃圾回收期準備還釋放對象佔用的存儲空間,將首先調用其finalize()方法,而且在下一次垃圾回收動做發生時,纔會真正回收對象佔用的內存。finalize()方法用於釋放對象中並不是使用new得到的特殊內存區域(由於垃圾回收期只知道釋放那些由new分配的內存),這種特殊的內存區域通常是由 本地方法建立的。

 

做者提到能夠在finalize()方法中加入一些判斷,這樣對象在終結前若不處於正確的狀態,能夠處理這種錯誤或者提醒程序員。在示例程序中做者調用了System.gc()方法來使得對象的finalize()能夠被調用,關於System.gc()它 只是提醒虛擬機:程序員但願進行一次垃圾回收。可是它不能保證垃圾回收必定會進行,並且具體何時進行是取決於具體的虛擬機的,不一樣的虛擬機有不一樣的對策。本身寫程序測試了一下,new了一個對象而後調用System.gc()並無使JVM調用垃圾回收器。new了一個對象的數組,當元素達到十萬時開始有對象的finalize()方法被調用了。

 

在重寫finalize()方法時應該老是假設基類的finalize()也要作某些重要的事情,所以有必要使用super調用基類的finalize()方法。

 

練習11要求寫一個程序讓finalize()總會被調用,在習題答案中連續使用了

System.gc();

System.runFinalization();

兩個方法。做者說連續調用這兩個方法會盡最大可能運行垃圾回收但並不必定。不一樣版本的JDK運行垃圾回收的行爲會不一樣,調用這些方法僅僅是一個請求,並不能保證垃圾回收必定會運行。其實並無方法能保證finalize()方法必定會被調用。

 

 

垃圾回收器通常有「標記-清掃」和「中止-複製」兩種方式,若是有大量對象須要回收或碎片空間較多,就採用「中止-複製」模式,若是程序運行穩定垃圾較少則採用「標記-清掃」的方式,虛擬機會在合適的時間切換兩種方式。

 

JVM中有一種即時(Just-In-Time,JIT)編譯器的技術,這種技術能夠把程序所有或部分翻譯成本地機器碼。當須要裝載某個類時,編譯器會先找到.class文件而後將字節碼裝入內存。此時有兩種方案可供選擇,一種讓即時編譯器編譯全部加載的字節碼,這種作法有兩個缺陷,一是加載就需翻譯所有加載代碼增長額外開銷,二是增長了可執行代碼的長度(字節碼要比即時編譯器翻譯後的本地機器碼小不少),這將致使頁面調度,從而下降程序速度。另一種作法成爲惰性評估(lazy evaluation),即時編譯器只在必要的時候才編譯代碼。這樣不被執行的代碼不會被JIT編譯。HotSpot採用了類型的方法。

 

5.7

靜態初始化只有在必要時刻纔會進行。

初始化的順序是先靜態對象,然後是非靜態對象。

 

對象建立過程的總結,假設有個名爲Dog的類:

一、當首次建立類型爲Dog的對象或者Dog類的靜態方法/靜態域首次被訪問時,Java解釋器查找類路徑,定位Dog.class文件;

二、載入Dog.class(這將建立一個Class對象),有關靜態初始化的全部動做都會執行。所以,靜態初始化只在Class對象首次加載的時候進行一次;

三、當用new Dog()建立對象的時候,首先將在堆上爲Dog對象分配足夠的存儲空間;

四、這塊存儲空間會被清零,這就自動將Dog對象中的全部基本類型數據都設置成了默認值;

五、執行全部出現於字段定義處的初始化動做;

六、執行構造函數。

 

練習14中讓類中的兩個靜態字段,一個在定義處初始化,一個在靜態塊中初始化。通過編程驗證,定義處初始化先於靜態塊中的初始化。

 

可使用非靜態語句塊初始化字段,方法與靜態相同,只是去掉語句塊前的static便可。通過編程驗證,類中能夠有多個語句塊,執行的順序按照語句塊出如今代碼中的順序。

語句塊執行在定義處初始化以後,構造函數執行。

 

5.8

用一對花括號括起來的值給數組初始化, 只能在建立數組的地方使用

Arrays.toString(array)方法產生數組的能夠打印版本。

若是想在非定義的時候初始化數組,可採用如下的形式:

int[] ints;

ints = new int[] {1,2,3};

此種方法是錯誤的:ints = {1,2,3};

 

在1.5以後加入了可變的參數列表。指定參數時,編譯器會自動去填充數組,得到的參數仍舊是個數組,能夠用foreach來迭代。若是編譯器發現參數已是一個數組,則不會作任何的轉換。可變長參數能夠接受0個參數。

P102下部的示例中倒數第二行:

printArray((Object[]) new Integer[]{1,2,3,4,5});

以前一直認爲數組是不能這麼轉換的,編程測試了一下是能夠的,應該是向上轉換能夠即Integer[]轉成Object[],而Object[]轉Integer[]是不合法的,運行時報異常。

 

getClass()方法由System.out.println()打印出的信息中,前導的[表示是數組,其後是類名,若是是I表示是基本類型int。

 

可變長參數列表不依賴於自動包裝機制,可使用基本類型。可是,可變長參數依舊能夠在必要的時候使用自動包裝機制。

 

若是有多個可變長參數列表的重載函數,調用無參數的函數,編譯器將不知道究竟要調用哪個方法,而產生錯誤。

 

因爲可變長參數列表與類型提高、自動包裝等特性混合在一塊兒使程序變得難以理解且不安全,因此做者推薦: 應該老是隻在一個重載方法上使用可變長參數列表,或者壓根不用。

 

從練習20可知,主函數的參數能夠寫成可變長參數列表的形式:

public static void main(String... args)

 

5.9

定義枚舉

public enum Spiciness {

 NOT, MILD, MEDIUM, HOT, FLAMING

}

爲了使用枚舉,須要建立該類型的一個引用,而後賦值:

Spiciness howHot = Spiciness.MEDIUM;

在建立enum時,編譯器會自動添加一些有用的特性。如,它會建立toString()方法,以方便顯示某個enum實例的名字,還會建立ordinal()方法,用來表示某個特定enum常量的聲明順序(也能夠理解成enum常量所表明的值),以及static values()方法,用來按照enum常量的聲明順序,產生由這些常量值構成的數組。

 

儘管enum看起來像是一種新的數據類型,可是這個關鍵字只是爲enum生成對應的類,而且產生了一些編譯器行爲,能夠將enum看成類來處理。

相關文章
相關標籤/搜索