程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 《Effective Java》學習筆記(1)

《Effective Java》學習筆記(1)

編輯:關於JAVA

這本書對於java程序員的意義就如《Effective C++》對於C++程序員的意義一樣,我想是每個Java愛好者的必讀書之一了,最近在啃這本書,一些學習筆記希望能對大家有所幫助。

一。創建和銷毀對象

第一條:考慮用靜態工廠方法代替構造函數

實例代碼 :Boolean類中的valueOf()方法

public static Boolean valueOf(boolean b)

{

return (b?Boolean.TRUE:Boolean.FALSE);

}

優點:

1。和構造函數不同,靜態工廠方法有自己的名字。如果存在著多種版本的構造函數,有的僅僅是參數順便的不同,此時你應該考慮用靜態工廠方法。

2。靜態工廠方法不要求一定要創建對象。可使用預先構造好的對象。例如Boolean.valueOf()方法就從不創建對象。在需要頻繁創建對象,並且創建對象成本較高的情況下,你應該考慮采用靜態工廠方法

3。與構造函數不同,靜態工廠方法可以返回一個原返回類型的子類型的對象。這方面的最好的例子就是Collections Framework。Collections Framework有20個實用的集合接口實現,這些實現大多數是通過一個不可實例子化的Java.utl.Collections中的靜態工廠方法導出的。

缺點:

1。類如果不含有公有或者受保護的構造函數,就不能被繼承。某種意義上這也限制了繼承的濫用

2。靜態工廠方法和其他靜態方法一樣,一般要在API文檔中作出特別的說明。在沒有強烈的需要下,你還是應該使用規范的構造函數。

第2條:使用私有構造函數強化singleton屬性

所謂singleton是指這樣的類,它只能被實例化一次/(也就是單例模式),有兩種方式,如下:

1。提供一個靜態常量

public class Example{

public static final Example INSTANCE=new Example();

private Example(){ //構造函數為私有

...}

....

}

2。使用靜態工廠方法

public class Example{

private static final Example INSTANCE=new Example();//改為私有

private Example(){ //構造函數為私有

...}

public static Example getInstance(){

return INSTANCE;

}

....

}

第一種方法在性能上可能更好,第2種方法提供了更大的靈活性,你可以決定是否做成singleton。要使一個singleton的類變成可序列化的,僅僅實現Serializable接口是不夠,還必須提供一個readResolve()方法,否則會產生一個新的實例。違背了singleton的本意

private Object readResolve() throws ObjectStreamException{

return INSTANCE;

}

第3條。通過私有構造函數強化不可實例能力

也就是不使某個類不能產生任何對象。或者你要說寫成抽象類不就可以了?NO,抽象類可以被實現,其子類也可以被實現。我們要的是絕對不能被實例化的類,這種類一般只有一些靜態變量和靜態方法,只是作為工具類使用,如Java.utl.Arrays。要做到這一點只要包含一個私有的顯式構造函數。這樣同時也保證了這個類不能被繼承,因為子類無法訪問父類的構造函數。

第4條:避免重復創建對象

如果一個對象是非可變的,那麼它總可以被重用,而不是再去創建一個對象。例如

String s=new String("denny");

裡面的"denny"本身就是一個實例。而這句話每次又重新創建一個同樣的實例。這完全是沒有必要的,如果在一個頻繁調用的方法中使用這樣的語句,性能上會有很大影響。應該用

String s="denny";來代替上面的語句。一個常用的方法是把重復需要用到的對象做成類的私有的靜態常量(當然,要保證這些變量在創建以後不再改變),用一個static塊包含他們。另外,不要以為創建對象是代價非常昂貴,相反,一些小對象的構造函數往往只做很少的工作,所以小對象的創建是非常廉價的,只有重量級的對象(如數據庫連接)才需要采用對象池來重用對象。

第5條:消除過期引用

“內存洩露”!什麼,我有沒有聽錯,Java也有“內存洩露”。是的,那不是C++的專利。看下面的例子

public Class Stack{

private Object[] elements;

private in size=0;

public Stack(int initialCapacity){

this.elements=new Object[initialCapacity];

}

public Object pop()

{

if(size==0)

throw new EmptyStackException();

return elements[--size];

}

....

} 這個程序並沒有很明顯的錯誤,但是隨著不斷增加的內存占用,程序的性能的降低會逐漸顯現。原因在於這個棧收縮的時候,從棧中彈出的對象並不會被當作垃圾回收,這是因為棧內部維持著這些對象的過期引用,也就是永遠也不會再被解除的引用,應該把pop操作修改下:

public Object pop()

{

if(size==0)

throw new EmptyStackException();

Object result=elements[--size];

elements[size]==null; //把引用設為null

return result;

}

自己管理內存的類一般都存在著這樣的問題,必須時刻警惕。內存洩露的另一個來源就是緩存了,你緩存了一個對象,卻忘記了去釋放。內存洩露問題可以通過專門的工具來檢測。

第6條:避免使用終結函數(finalize())

想起一次在CSDN論壇上,有人問什麼時候該使用finlize(),和C++有什麼不同,我竟然回答說可以在finalize()方法中處理一些關閉資源的操作(關閉文件等等)。汗顏!終結函數並不能保證會被及時地執行,從一個對象變的不可到達(通過對象網絡沒有了這個對象的引用),到它的終結函數被執行,這段時間的長短是任意的,不確定的。所以,時間關鍵的任務不應該由終結函數來完成,例如關閉一個已經被打開的文件。由於JVM延遲執行終結函數,所以大量的文件保留在打開狀態!而且終結函數的實現是不同的JVM中有不同的方法,所以你不能保證此函數的移植性。記住這點:

我們不應該依賴一個終結函數來更新關鍵性的永久狀態。

那麼我們該如何編程序來執行清理工作,通常提供一個顯式的終止方法,通常與tr..finally結構結合使用,這方面的例子最好的是Java.io裡面的各種流操作了,基本都有一個close()方法,你必須顯式地關閉打開的資源。終結函數的使用有兩個合理的方面:

1。充當最後一道“安全網”,在客戶端忘記或者不能調用顯式終止方法的時候。

2。調用本地對象的時候,本地對象不擁有關鍵性資源的前提下,終止方法完成必要的工作以釋放資源。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved