日本搞逼视频_黄色一级片免费在线观看_色99久久_性明星video另类hd_欧美77_综合在线视频

國內最全IT社區平臺 聯系我們 | 收藏本站
阿里云優惠2
您當前位置:首頁 > php開源 > php教程 > Effective Java (創建和銷毀對象)

Effective Java (創建和銷毀對象)

來源:程序員人生   發布時間:2017-02-07 09:05:04 閱讀次數:3388次

學習Java的同學注意了!!! 
學習進程中遇到甚么問題或想獲得學習資源的話,歡迎加入Java學習交換群,群號碼:183993990  我們1起學Java!


1、斟酌用靜態工廠方法代替構造器:
    
      構造器是創建1個對象實例最基本也最通用的方法,大部份開發者在使用某個class的時候,首先需要斟酌的就是如何構造和初始化1個對象示例,而構造的方式首先斟酌到的就是通過構造函數來完成,因此在看javadoc中的文檔時首先關注的函數也是構造器。但是在有些時候構造器并不是我們唯1的選擇,通過反射也是可以輕松到達的。我們這里主要提到的方式是通過靜態類工廠的方式來創建class的實例,如:

1 public static Boolean valueOf(boolean b) { 2 return b ? Boolean.TRUE : Boolean.FALSE; 3 }

      靜態工廠方法和構造器不同有以下主要優勢:
      1.    成心義的名稱。
      在框架設計中,針對某些工具類通常會斟酌dummy對象或空對象以辨別該對象是不是已被初始化,如我曾在我的C++基礎庫中實現了String類型,見以下代碼:

復制代碼
 1 void showExample() {  2 String strEmpty = String::empty();  3 String strEmpty2 = "";  4 String strData = String::prellocate(1024);  5 if (strEmpty.isEmpty()) {  6 //TODO: do something  7 }  8 }  9 static String String::emptyString; 10 String& String::empty() { 11 return emptyString; 12 } 13 14 bool String::isEmpty() { 15 if (this->_internal == &emptyString->_internal) 16 return true; 17 //TODO: do other justice to verify whether it is empty. 18 }
復制代碼

      在上面的代碼中,提供了兩個靜態工廠方法empty和preallocate用于分別創建1個空對象和1個帶有指定分配空間的String對象。從使用方式來看,這些靜態方法確切提供了成心義的名稱,使用者很容易就能夠判斷出它們的作用和利用場景,而沒必要在1組重載的構造器中去搜索每個構造函數及其參數列表,以找出合適當前場景的構造函數。從效力方面來說,由于提供了唯1的靜態空對象,當判讀對象實例是不是為空時(isEmpty),直接使用預制靜態空對象(emptyString)的地址與當前對象進行比較,如果是同1地址,便可確認當前實例為空對象了。對preallocate函數,顧名思義,該函數預分配了指定大小的內存空間,后面在使用該String實例時,沒必要擔心賦值或追加的字符過量而致使頻繁的realloc等操作。    
      2.    沒必要在每次調用它們的時候創建1個新的對象。
      還是基于上面的代碼實例,由于所有的空對象都同享同1個靜態空對象,這樣也節省了更多的內存開消,如果是strEmpty2方式構造出的空對象,在履行比較等操作時會帶來更多的效力開消。事實上,Java在String對象的實現中,使用了常量資源池也是基于了一樣的優化策略。該優勢一樣適用于單實例模式。  
      3.    可以返回原返回類型的任何子類型。
      在Java Collections Framework的集合接口中,提供了大量的靜態方法返回集合接口類型的實現類型,如Collections.subList()、Collections.unmodifiableList()等。返回的接口是明確的,但是針對具體的實現類,函數的使用者其實不也無需知曉。這樣不但極大的減少了導出類的數量,而且在今后如果發現某個子類的實現效力較低或發現更好的數據結構和算法來替換當前實現子類時,對集合接口的使用者來講,不會帶來任何的影響。本書在例子中提到EnumSet是通過靜態工廠方法返回對象實例的,沒有提供任何構造函數,其內部在返回實現類時做了1個優化,即如果枚舉的數量小于64,該工廠方法將返回1個經過特殊優化的實現類實例(RegularEnumSet),其內部使用long(64bits在Java中) 中的不同位來表示不同的枚舉值。如果枚舉的數量大于64,將使用long的數組作為底層支持。但是這些內部實現類的優化對使用者來講是透明的。 
      4.    在創建參數化類型實例的時候,它們使代碼變得更加簡潔。
      Map m = new HashMap();
      由于Java在構造函數的調用中沒法進行類型的推演,因此也就沒法通過構造器的參數類型來實例化指定類型參數的實例化對象。但是通過靜態工廠方法則可以利用參數類型推演的優勢,避免了類型參數在1次聲明中被屢次重寫所帶來的煩憂,見以下代碼:
      public static HashMap newInstance() {
          return new HashMap();
      }
      Map m = MyHashMap.newInstance();

    
2、遇到多個構造參數時要斟酌用構建器(Builder模式):
    
      如果1個class在構造初始化的時候存在非常多的參數,將會致使構造函數或靜態工廠函數帶有大量的、類型相同的函數參數,特別是當1部份參數只是可選參數的時候,class的使用者不能不為這些可選參數也傳入缺省值,有的時候會發現使用者傳入的缺省值多是成心義的,而并不是class內部實現所認可的缺省值,比如某個整型可選參數,通常使用者會傳入0,然后class內部的實現恰恰認為0是1種重要的狀態,而該狀態其實不是該調用者關心的,但是該狀態卻間接致使其他狀態的改變,因此帶來了1些潛伏的狀態不1致問題。與此同時,過量的函數參數也給使用者的學習和使用帶來很多沒必要要的麻煩,我相信任何使用者都希望看到class的接口是簡單易用、函數功能清晰可見的。在Effective C++中針對接口的設計有這樣的1句話:"接口要完滿而最小化"。針對該類問題通常會斟酌的方法是將所有的參數歸結到1個JavaBean對象中,實例化這個Bean對象,然后再將實例化的結果傳給這個class的構造函數,這類方法依然沒有避免缺省值的問題。該條目推薦了Builder模式來創建這個帶有很多可選參數的實例對象。

復制代碼
 1 class NutritionFacts {  2 private final int servingSize;  3 private final int servings;  4 private final int calories;  5 private final int fat;  6 private final int sodium;  7 private final int carbohydrate;  8 public static class Builder {  9 //對象的必選參數 10 private final int servingSize; 11 private final int servings; 12 //對象的可選參數的缺省值初始化 13 private int calories = 0; 14 private int fat = 0; 15 private int carbohydrate = 0; 16 private int sodium = 0; 17 //只用少數的必選參數作為構造器的函數參數 18 public Builder(int servingSize,int servings) { 19 this.servingSize = servingSize; 20 this.servings = servings; 21 } 22 public Builder calories(int val) { 23 calories = val; 24 return this; 25 } 26 public Builder fat(int val) { 27 fat = val; 28 return this; 29 } 30 public Builder carbohydrate(int val) { 31 carbohydrate = val; 32 return this; 33 } 34 public Builder sodium(int val) { 35 sodium = val; 36 return this; 37 } 38 public NutritionFacts build() { 39 return new NutritionFacts(this); 40 } 41 } 42 private NutritionFacts(Builder builder) { 43 servingSize = builder.servingSize; 44 servings = builder.servings; 45 calories = builder.calories; 46 fat = builder.fat; 47 sodium = builder.sodium; 48 carbohydrate = builder.carbohydrate; 49 } 50 } 51 //使用方式 52 public static void main(String[] args) { 53 NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8).calories(100) 54 .sodium(35).carbohydrate(27).build(); 55 System.out.println(cocaCola); 56 }
復制代碼

      對Builder方式,可選參數的缺省值問題也將不再困擾著所有的使用者。這類方式還帶來了1個間接的好處是,不可變對象的初始化和參數合法性的驗證等工作在構造函數中原子性的完成了。

3、用私有構造器或枚舉類型強化Singleton屬性:

      對單實例模式,相信很多開發者其實不陌生,但是如何更好更安全的創建單實例對象還是需要1些斟酌和考慮的,在Java中主要的創建方式有以下3種,我們分別作出解釋和適當的比較。
      1.    將構造函數私有化,直接通過靜態公有的final域字段獲得單實例對象:

1 public class Elvis { 2 public static final Elvis INSTANCE = new Elvis(); 3 private Elivs() { ... } 4 public void leaveTheBuilding() { ... } 5 }

      這樣的方式主要優勢在于簡潔高效,使用者很快就可以判定當前類為單實例類,在調用時直接操作Elivs.INSTANCE便可,由于沒有函數的調用,因此效力也非常高效。但是事物是具有1定的雙面性的,這類設計方式在1個方向上走的過于極端了,因此他的缺點也會是非常明顯的。如果今后Elvis的使用代碼被遷移到多線程的利用環境下了,系統希望能夠做到每一個線程使用同1個Elvis實例,不同線程之間則使用不同的對象實例。那末這類創建方式將沒法實現該需求,因此需要修改接口和接口的調用者代碼,這樣就帶來了更高的修改本錢。
      2.    通過公有域成員的方式返回單實例對象:

1 public class Elvis { 2 public static final Elvis INSTANCE = new Elvis(); 3 private Elivs() { ... } 4 public static Elvis getInstance() { return INSTANCE; } 5 public void leaveTheBuilding() { ... } 6 }

      這類方法很好的彌補了第1種方式的缺點,如果今后需要適應多線程環境的對象創建邏輯,僅需要修改Elvis的getInstance()方法內部便可,對用調用者而言則是不變的,這樣便極大的縮小了影響的范圍。至于效力問題,當今的JVM針對該種函數都做了很好的內聯優化,因此不會產生因函數頻繁調用而帶來的開消。
      3.    使用枚舉的方式(Java SE5):

1 public enum Elvis { 2 INSTANCE; 3 public void leaveTheBuilding() { ... } 4 }

      就目前而言,這類方法在功能上和公有域方式相近,但是他更加簡潔更加清晰,擴大性更強也更加安全。
      我在設計自己的表達式解析器時,曾將所有的操作符設計為enum中不同的枚舉元素,同時提供了帶有參數的構造函數,傳入他們的優先級、操作符名稱等信息。

4、通過私有構造器強化不可實例化的能力:

      對有些工具類如java.lang.Math、java.util.Arrays等,其中只是包括了靜態方法和靜態域字段,因此對這樣的class實例化就顯得沒有任何意義了。但是在實際的使用中,如果不加任何特殊的處理,這樣的classes是可以像其他classes1樣被實例化的。這里介紹了1種方式,既將缺省構造函數設置為private,這樣類的外部將沒法實例化該類,與此同時,在這個私有的構造函數的實現中直接拋出異常,從而也避免了類的內部方法調用該構造函數。

1 public class UtilityClass { 2 //Suppress default constructor for noninstantiability. 3 private UtilityClass() { 4 throw new AssertionError(); 5 } 6 }

      這樣定義以后,該類將不會再被外部實例化了,否則會產生編譯毛病。但是這樣的定義帶來的最直接的負面影響是該類將不能再被子類化。
    
5、避免創建沒必要要的對象:

      試比較以下兩行代碼在被屢次反復履行時的效力差異:
      String s = new String("stringette");
      String s = "stringette";

      由于String被實現為不可變對象,JVM底層將其實現為常量池,既所有值等于"stringette" 的String對象實例同享同1對象地址,而且還可以保證,對所有在同1JVM中運行的代碼,只要他們包括相同的字符串字面常量,該對象就會被重用。
    
      我們繼續比較下面的例子,并測試他們在運行時的效力差異:
      Boolean b = Boolean.valueOf("true");
      Boolean b = new Boolean("true");
      前者通過靜態工廠方法保證了每次返回的對象,如果他們都是true或false,那末他們將返回相同的對象。換句話說,valueOf將只會返回Boolean.TRUE或Boolean.FALSE兩個靜態域字段之1。而后面的Boolean構造方式,每次都會構造出1個新的Boolean實例對象。這樣在屢次調用后,第1種靜態工廠方法將會避免大量沒必要要的Boolean對象被創建,從而提高了程序的運行效力,也下降了垃圾回收的負擔。  
      繼續比較下面的代碼:

復制代碼
 1 public class Person {  2 private final Date birthDate;  3 //判斷該嬰兒是不是是在生育高峰期誕生的。  4 public boolean isBabyBoomer {  5 Calender c = Calendar.getInstance(TimeZone.getTimeZone("GMT"));  6 c.set(1946,Calendar.JANUARY,1,0,0,0);  7 Date dstart = c.getTime();  8 c.set(1965,Calendar.JANUARY,1,0,0,0);  9 Date dend = c.getTime(); 10 return birthDate.compareTo(dstart) >= 0 && birthDate.compareTo(dend) < 0; 11 } 12 } 13 14 public class Person { 15 private static final Date BOOM_START; 16 private static final Date BOOM_END; 17 18 static { 19 Calender c = Calendar.getInstance(TimeZone.getTimeZone("GMT")); 20 c.set(1946,Calendar.JANUARY,1,0,0,0); 21 BOOM_START = c.getTime(); 22 c.set(1965,Calendar.JANUARY,1,0,0,0); 23 BOOM_END = c.getTime(); 24 } 25 public boolean isBabyBoomer() { 26 return birthDate.compareTo(BOOM_START) >= 0 && birthDate.compareTo(BOOM_END) < 0; 27 } 28 }
復制代碼

      改進后的Person類只是在初始化的時候創建Calender、TimeZone和Date實例1次,而不是在每次調用isBabyBoomer方法時都創建1次他們。如果該方法會被頻繁調用,效力的提升將會極其顯著。
      集合框架中的Map接口提供keySet方法,該方法每次都將返回底層原始Map對象鍵數據的視圖,而其實不會為該操作創建1個Set對象并填充底層Map所有鍵的對象拷貝。因此當屢次調用該方法并返回不同的Set對象實例時,事實上他們底層指向的將是同1段數據的援用。
      在該條目中還提到了自動裝箱行動給程序運行帶來的性能沖擊,如果可以通過原始類型完成的操作應當盡可能避免使用裝箱類型和他們之間的交互使用。見下例:

復制代碼
1 public static void main(String[] args) { 2 Long sum = 0L; 3 for (long i = 0; i < Integer.MAX_VALUE; ++i) { 4 sum += i; 5 } 6 System.out.println(sum); 7 }
復制代碼

      本例中由于錯把long sum定義成Long sum,其效力下降了近10倍,這其中的主要緣由便是該毛病致使了2的31次方個臨時Long對象被創建了。

6、消除過期的對象援用:

      雖然Java不像C/C++那樣需要手工管理內存資源,而是通過更加方便、更加智能的垃圾回收機制來幫助開發者清算過期的資源。即使如此,內存泄漏問題依然會產生在你的程序中,只是和C/C++相比,Java中內存泄漏更加藏匿,更加難以發現,見以下代碼:

復制代碼
 1 public class Stack {  2 private Object[] elements;  3 private int size = 0;  4 private static final int DEFAULT_INITIAL_CAPACITY = 16;  5 public Stack() {  6 elements = new Object[DEFAULT_INITIAL_CAPACITY];  7 }  8 public void push(Object e) {  9 ensureCapacity(); 10 elements[size++] = e; 11 } 12 public Object pop() { 13 if (size == 0) 14 throw new EmptyStackException(); 15 return elements[--size]; 16 } 17 private void ensureCapacity() { 18 if (elements.length == size) 19 elements = Arrays.copys(elements,2*size+1); 20 } 21 }
復制代碼

      以上示例代碼,在正常的使用中不會產生任何邏輯問題,但是隨著程序運行時間不斷加長,內存泄漏釀成的副作用將會漸漸的顯現出來,如磁盤頁交換、OutOfMemoryError等。那末內存泄漏隱藏在程序中的甚么地方呢?當我們調用pop方法是,該方法將返回當前棧頂的elements,同時將該棧的活動區間(size)減1,但是此時被彈出的Object依然保持最少兩處援用,1個是返回的對象,另外一個則是該返回對象在elements數組中原有棧頂位置的援用。這樣即使外部對象在使用以后不再援用該Object,那末它依然不會被垃圾搜集器釋放,長此以往致使了更多類似對象的內存泄漏。修改方式以下:

復制代碼
1 public Object pop() { 2 if (size == 0) 3 throw new EmptyStackException(); 4 Object result = elements[--size]; 5 elements[size] = null; //手工將數組中的該對象置空 6 return result; 7 }
復制代碼

      由于現有的Java垃圾搜集器已足夠只能和強大,因此沒有必要對所有不在需要的對象履行obj = null的顯示置空操作,這樣反而會給程序代碼的瀏覽帶來沒必要要的麻煩,該條目只是推薦在以下3中情形下需要斟酌資源手工處理問題:
      1)    類是自己管理內存,如例子中的Stack類。
      2)    使用對象緩存機制時,需要斟酌被從緩存中換出的對象,或是長時間不會被訪問到的對象。
      3)    事件監聽器和相干回調。用戶常常會在需要時顯示的注冊,但是卻常常會忘記在不用的時候注銷這些回調接口實現類。
    
7、避免使用終結方法:

      任何事情都存在其1定的雙面性或多面性,對C++的開發者,內存資源是需要手工分配和釋放的,而對Java和C#這類資源托管的開發語言,更多的工作可以交給虛擬機的垃圾回收器來完成,由此C++程序得到了運行效力,卻失去了安全。在Java的實際開發中,并不是所有的資源都是可以被垃圾回收器自動釋放的,如FileInputStream、Graphic2D等class中使用的底層操作系統資源句柄,其實不會隨著對象實例被GC回收而被釋放,但是這些資源對全部操作系統而言,都是非常重要的稀缺資源,更多的資源句柄泄漏將會致使全部操作系統及其運行的各種服務程序的運行效力直線降落。那末如何保證系統資源不會被泄漏了?在C++中,由于其資源完全交由開發者自行管理,因此在決定資源什么時候釋放的問題上有著很優雅的支持,C++中的析構函數可以說是完成這1工作的天然候選者。任何在棧上聲明的C++對象,當棧退出或當前對象離開其作用域時,該對象實例的析構函數都會被自動調用,因此當函數中有任何異常(Exception)產生時,在棧被燒毀之前,所有棧對象的析構函數均會被自動調用。但是對Java的開發者而言,從語言本身視角來看,Java本身并未提供析構函數這樣的機制,固然這也是和其資源被JVM托管有1定關系的。
      在Java中完成這樣的工作主要是依托try-finally機制來協助完成的。但是Java中還提供了另外1種被稱為finalizer的機制,使用者僅僅需要重載Object對象提供的finalize方法,這樣當JVM的在進行垃圾回收時,就能夠自動調用該方法。但是由于對象什么時候被垃圾搜集的不肯定性,和finalizer給GC帶來的性能上的影響,因此其實不推薦使用者依托該方法來到達關鍵資源釋放的目的。比如,有數千個圖形句柄都在等待被終結和回收,惋惜的是履行終結方法的線程優先級要低于普通的工作者線程,這樣就會有大量的圖形句柄資源停留在finalizer的隊列中而不能被及時的釋放,終究致使了系統運行效力的降落,乃至還會引發JVM報出OutOfMemoryError的毛病。
      Java的語言規范中并沒有保證該方法會被及時的履行,乃至都沒有保證1定會被履行。即使開發者在code中手工調用了System.gc和System.runFinalization這兩個方法,這僅僅是提高了finalizer被履行的概率而已。還有1點需要注意的是,被重載的finalize()方法中如果拋出異常,其棧幀軌跡是不會被打印出來的。在Java中被推薦的資源釋放方法為,提供顯式的具有良好命名的接口方法,如FileInputStream.close()和Graphic2D.dispose()等。然后使用者在finally區塊中調用該方法,見以下代碼:

復制代碼
1 public void test() { 2 FileInputStream fin = null; 3 try { 4 fin = new FileInputStream(filename); 5 //do something. 6 } finally { 7 fin.close(); 8 } 9 }
復制代碼

      那末在實際的開發中,利用finalizer又能給我們帶來甚么樣的幫助呢?見下例:

復制代碼
 1 public class FinalizeTest {  2 //@Override  3 protected void finalize() throws Throwable {  4 try {  5 //在調試進程中通過該方法,打印對象在被搜集前的各種狀態,  6 //如判斷是不是仍有資源未被釋放,或是不是有狀態不1致的現象存在。  7 //推薦將該finalize方法設計成僅在debug狀態下可用,而在release  8 //下該方法其實不存在,以免其對運行時效力的影響。  9 System.out.println("The current status: " + _myStatus); 10 } finally { 11 //在finally中對超類finalize方法的調用是必須的,這樣可以保證全部class繼承 12 //體系中的finalize鏈都被履行。 13 super.finalize(); 14 } 15 } 16 }
復制代碼





生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關閉
程序員人生
主站蜘蛛池模板: 99精品一区二区 | 精品久久久国产 | 国产欧美日韩综合精品一区二区 | 久久久久久亚洲精品 | 99久久99 | 国产在线视频一区 | 成人午夜精品 | 亚洲 自拍 另类 欧美 丝袜 | 爱爱免费 | 国产午夜精品视频 | 国产精品hongkong在线 | 色婷婷香蕉在线一区二区 | 亚洲视频中文字幕 | 国产成人精品免费视频大全最热 | 午夜免费 | 麻豆视频一区二区 | 久久成人亚洲 | 欧美激情xxxxx| 国产精品视频久久久 | 国产欧美91 | 国产成人精品在线观看 | 欧美日韩亚洲一区二区三区 | 久久中文在线 | 亚洲视频高清 | 免费在线黄色电影 | 日本精品免费 | 日韩成人在线视频 | 成人h动漫精品一区二区器材 | 99国产精品视频免费观看一公开 | 日韩福利在线 | 欧美精选一区二区 | 亚洲视频二 | 欧美日韩一区二区三区视频 | 狠狠干网站| 国产高清一区二区三区 | 中文字字幕码日产高清 | 国产一区二区三区成人 | 黑人巨大精品欧美一区免费视频 | 国产精品久久久久久久久久久久午夜片 | 蜜乳视频 | 毛片在线视频 |