成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Effective Java 創(chuàng)建和銷(xiāo)毀對(duì)象

開(kāi)發(fā) 后端
構(gòu)造器是創(chuàng)建一個(gè)對(duì)象實(shí)例最基本也最通用的方法,大部分開(kāi)發(fā)者在使用某個(gè)class的時(shí)候,首先需要考慮的就是如何構(gòu)造和初始化一個(gè)對(duì)象示例,而構(gòu)造的方式首先考慮到的就是通過(guò)構(gòu)造函數(shù)來(lái)完成,因此在看javadoc中的文檔時(shí)首先關(guān)注的函數(shù)也是構(gòu)造器。

一、考慮用靜態(tài)工廠(chǎng)方法代替構(gòu)造器:

構(gòu)造器是創(chuàng)建一個(gè)對(duì)象實(shí)例最基本也最通用的方法,大部分開(kāi)發(fā)者在使用某個(gè)class的時(shí)候,首先需要考慮的就是如何構(gòu)造和初始化一個(gè)對(duì)象示例,而構(gòu)造的方式首先考慮到的就是通過(guò)構(gòu)造函數(shù)來(lái)完成,因此在看javadoc中的文檔時(shí)首先關(guān)注的函數(shù)也是構(gòu)造器。然而在有些時(shí)候構(gòu)造器并非我們***的選擇,通過(guò)反射也是可以輕松達(dá)到的。我們這里主要提到的方式是通過(guò)靜態(tài)類(lèi)工廠(chǎng)的方式來(lái)創(chuàng)建class的實(shí)例,如:

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

靜態(tài)工廠(chǎng)方法和構(gòu)造器不同有以下主要優(yōu)勢(shì):

1. 有意義的名稱(chēng)。

在框架設(shè)計(jì)中,針對(duì)某些工具類(lèi)通常會(huì)考慮dummy對(duì)象或者空對(duì)象以辨別該對(duì)象是否已經(jīng)被初始化,如我曾在我的C++基礎(chǔ)庫(kù)中實(shí)現(xiàn)了String類(lèi)型,見(jiàn)如下代碼:

  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.      } 

在上面的代碼中,提供了兩個(gè)靜態(tài)工廠(chǎng)方法empty和preallocate用于分別創(chuàng)建一個(gè)空對(duì)象和一個(gè)帶有指定分配空間的String對(duì)象。從使用方式來(lái)看,這些靜態(tài)方法確實(shí)提供了有意義的名稱(chēng),使用者很容易就可以判斷出它們的作用和應(yīng)用場(chǎng)景,而不必在一組重載的構(gòu)造器中去搜尋每一個(gè)構(gòu)造函數(shù)及其參數(shù)列表,以找出適合當(dāng)前場(chǎng)景的構(gòu)造函數(shù)。從效率方面來(lái)講,由于提供了***的靜態(tài)空對(duì)象,當(dāng)判讀對(duì)象實(shí)例是否為空時(shí)(isEmpty),直接使用預(yù)制靜態(tài)空對(duì)象(emptyString)的地址與當(dāng)前對(duì)象進(jìn)行比較,如果是同一地址,即可確認(rèn)當(dāng)前實(shí)例為空對(duì)象了。對(duì)于preallocate函數(shù),顧名思義,該函數(shù)預(yù)分配了指定大小的內(nèi)存空間,后面在使用該String實(shí)例時(shí),不必?fù)?dān)心賦值或追加的字符過(guò)多而導(dǎo)致頻繁的realloc等操作。

2. 不必在每次調(diào)用它們的時(shí)候創(chuàng)建一個(gè)新的對(duì)象。

還是基于上面的代碼實(shí)例,由于所有的空對(duì)象都共享同一個(gè)靜態(tài)空對(duì)象,這樣也節(jié)省了更多的內(nèi)存開(kāi)銷(xiāo),如果是strEmpty2方式構(gòu)造出的空對(duì)象,在執(zhí)行比較等操作時(shí)會(huì)帶來(lái)更多的效率開(kāi)銷(xiāo)。事實(shí)上,Java在String對(duì)象的實(shí)現(xiàn)中,使用了常量資源池也是基于了同樣的優(yōu)化策略。該優(yōu)勢(shì)同樣適用于單實(shí)例模式。

3. 可以返回原返回類(lèi)型的任何子類(lèi)型。

在Java Collections Framework的集合接口中,提供了大量的靜態(tài)方法返回集合接口類(lèi)型的實(shí)現(xiàn)類(lèi)型,如Collections.subList()、Collections.unmodifiableList()等。返回的接口是明確的,然而針對(duì)具體的實(shí)現(xiàn)類(lèi),函數(shù)的使用者并不也無(wú)需知曉。這樣不僅極大的減少了導(dǎo)出類(lèi)的數(shù)量,而且在今后如果發(fā)現(xiàn)某個(gè)子類(lèi)的實(shí)現(xiàn)效率較低或者發(fā)現(xiàn)更好的數(shù)據(jù)結(jié)構(gòu)和算法來(lái)替換當(dāng)前實(shí)現(xiàn)子類(lèi)時(shí),對(duì)于集合接口的使用者來(lái)說(shuō),不會(huì)帶來(lái)任何的影響。本書(shū)在例子中提到EnumSet是通過(guò)靜態(tài)工廠(chǎng)方法返回對(duì)象實(shí)例的,沒(méi)有提供任何構(gòu)造函數(shù),其內(nèi)部在返回實(shí)現(xiàn)類(lèi)時(shí)做了一個(gè)優(yōu)化,即如果枚舉的數(shù)量小于64,該工廠(chǎng)方法將返回一個(gè)經(jīng)過(guò)特殊優(yōu)化的實(shí)現(xiàn)類(lèi)實(shí)(RegularEnumSet),其內(nèi)部使用long(64bits在Java中) 中的不同位來(lái)表示不同的枚舉值。如果枚舉的數(shù)量大于64,將使用long的數(shù)組作為底層支撐。然而這些內(nèi)部實(shí)現(xiàn)類(lèi)的優(yōu)化對(duì)于使用者來(lái)說(shuō)是透明的。

4. 在創(chuàng)建參數(shù)化類(lèi)型實(shí)例的時(shí)候,它們使代碼變得更加簡(jiǎn)潔。

  1. Map<String,String> m = new HashMap<String,String>(); 

由于Java在構(gòu)造函數(shù)的調(diào)用中無(wú)法進(jìn)行類(lèi)型的推演,因此也就無(wú)法通過(guò)構(gòu)造器的參數(shù)類(lèi)型來(lái)實(shí)例化指定類(lèi)型參數(shù)的實(shí)例化對(duì)象。然而通過(guò)靜態(tài)工廠(chǎng)方法則可以利用參數(shù)類(lèi)型推演的優(yōu)勢(shì),避免了類(lèi)型參數(shù)在一次聲明中被多次重寫(xiě)所帶來(lái)的煩憂(yōu),見(jiàn)如下代碼:

  1. public static <K,V> HashMap<K,V> newInstance() {  
  2.           return new HashMap<K,V>();  
  3.       }  
  4.       Map<String,String> m = MyHashMap.newInstance(); 

二、遇到多個(gè)構(gòu)造參數(shù)時(shí)要考慮用構(gòu)建器(Builder模式):

如果一個(gè)class在構(gòu)造初始化的時(shí)候存在非常多的參數(shù),將會(huì)導(dǎo)致構(gòu)造函數(shù)或者靜態(tài)工廠(chǎng)函數(shù)帶有大量的、類(lèi)型相同的函數(shù)參數(shù),特別是當(dāng)一部分參數(shù)只是可選參數(shù)的時(shí)候,class的使用者不得不為這些可選參數(shù)也傳入缺省值,有的時(shí)候會(huì)發(fā)現(xiàn)使用者傳入的缺省值可能是有意義的,而并非class內(nèi)部實(shí)現(xiàn)所認(rèn)可的缺省值,比如某個(gè)整型可選參數(shù),通常使用者會(huì)傳入0,然后class內(nèi)部的實(shí)現(xiàn)恰恰認(rèn)為0是一種重要的狀態(tài),而該狀態(tài)并不是該調(diào)用者關(guān)心的,但是該狀態(tài)卻間接導(dǎo)致其他狀態(tài)的改變,因而帶來(lái)了一些潛在的狀態(tài)不一致問(wèn)題。與此同時(shí),過(guò)多的函數(shù)參數(shù)也給使用者的學(xué)習(xí)和使用帶來(lái)很多不必要的麻煩,我相信任何使用者都希望看到class的接口是簡(jiǎn)單易用、函數(shù)功能清晰可見(jiàn)的。在Effective C++中針對(duì)接口的設(shè)計(jì)有這樣的一句話(huà):"接口要完滿(mǎn)而最小化"。針對(duì)該類(lèi)問(wèn)題通常會(huì)考慮的方法是將所有的參數(shù)歸結(jié)到一個(gè)JavaBean對(duì)象中,實(shí)例化這個(gè)Bean對(duì)象,然后再將實(shí)例化的結(jié)果傳給這個(gè)class的構(gòu)造函數(shù),這種方法仍然沒(méi)有避免缺省值的問(wèn)題。該條目推薦了Builder模式來(lái)創(chuàng)建這個(gè)帶有很多可選參數(shù)的實(shí)例對(duì)象。

  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.              //對(duì)象的必選參數(shù)  
  10.              private final int servingSize;  
  11.              private final int servings;  
  12.              //對(duì)象的可選參數(shù)的缺省值初始化  
  13.              private int calories = 0;  
  14.              private int fat = 0;  
  15.              private int carbohydrate = 0;  
  16.              private int sodium = 0;  
  17.              //只用少數(shù)的必選參數(shù)作為構(gòu)造器的函數(shù)參數(shù)  
  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(2408).calories(100)  
  54.              .sodium(35).carbohydrate(27).build();  
  55.          System.out.println(cocaCola);  
  56.      } 

對(duì)于Builder方式,可選參數(shù)的缺省值問(wèn)題也將不再困擾著所有的使用者。這種方式還帶來(lái)了一個(gè)間接的好處是,不可變對(duì)象的初始化以及參數(shù)合法性的驗(yàn)證等工作在構(gòu)造函數(shù)中原子性的完成了。

三、用私有構(gòu)造器或者枚舉類(lèi)型強(qiáng)化Singleton屬性:

對(duì)于單實(shí)例模式,相信很多開(kāi)發(fā)者并不陌生,然而如何更好更安全的創(chuàng)建單實(shí)例對(duì)象還是需要一些推敲和斟酌的,在Java中主要的創(chuàng)建方式有以下三種,我們分別作出解釋和適當(dāng)?shù)谋容^。

1. 將構(gòu)造函數(shù)私有化,直接通過(guò)靜態(tài)公有的final域字段獲取單實(shí)例對(duì)象:

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

這樣的方式主要優(yōu)勢(shì)在于簡(jiǎn)潔高效,使用者很快就能判定當(dāng)前類(lèi)為單實(shí)例類(lèi),在調(diào)用時(shí)直接操作Elivs.INSTANCE即可,由于沒(méi)有函數(shù)的調(diào)用,因此效率也非常高效。然而事物是具有一定的雙面性的,這種設(shè)計(jì)方式在一個(gè)方向上走的過(guò)于極端了,因此他的缺點(diǎn)也會(huì)是非常明顯的。如果今后Elvis的使用代碼被遷移到多線(xiàn)程的應(yīng)用環(huán)境下了,系統(tǒng)希望能夠做到每個(gè)線(xiàn)程使用同一個(gè)Elvis實(shí)例,不同線(xiàn)程之間則使用不同的對(duì)象實(shí)例。那么這種創(chuàng)建方式將無(wú)法實(shí)現(xiàn)該需求,因此需要修改接口以及接口的調(diào)用者代碼,這樣就帶來(lái)了更高的修改成本。

2. 通過(guò)公有域成員的方式返回單實(shí)例對(duì)象:

  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.      } 

這種方法很好的彌補(bǔ)了***種方式的缺陷,如果今后需要適應(yīng)多線(xiàn)程環(huán)境的對(duì)象創(chuàng)建邏輯,僅需要修改Elvis的getInstance()方法內(nèi)部即可,對(duì)用調(diào)用者而言則是不變的,這樣便極大的縮小了影響的范圍。至于效率問(wèn)題,現(xiàn)今的JVM針對(duì)該種函數(shù)都做了很好的內(nèi)聯(lián)優(yōu)化,因此不會(huì)產(chǎn)生因函數(shù)頻繁調(diào)用而帶來(lái)的開(kāi)銷(xiāo)。

3. 使用枚舉的方式(Java SE5):

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

就目前而言,這種方法在功能上和公有域方式相近,但是他更加簡(jiǎn)潔更加清晰,擴(kuò)展性更強(qiáng)也更加安全。

我在設(shè)計(jì)自己的表達(dá)式解析器時(shí),曾將所有的操作符設(shè)計(jì)為enum中不同的枚舉元素,同時(shí)提供了帶有參數(shù)的構(gòu)造函數(shù),傳入他們的優(yōu)先級(jí)、操作符名稱(chēng)等信息。

四、通過(guò)私有構(gòu)造器強(qiáng)化不可實(shí)例化的能力:

對(duì)于有些工具類(lèi)如java.lang.Math、java.util.Arrays等,其中只是包含了靜態(tài)方法和靜態(tài)域字段,因此對(duì)這樣的class實(shí)例化就顯得沒(méi)有任何意義了。然而在實(shí)際的使用中,如果不加任何特殊的處理,這樣的classes是可以像其他classes一樣被實(shí)例化的。這里介紹了一種方式,既將缺省構(gòu)造函數(shù)設(shè)置為private,這樣類(lèi)的外部將無(wú)法實(shí)例化該類(lèi),與此同時(shí),在這個(gè)私有的構(gòu)造函數(shù)的實(shí)現(xiàn)中直接拋出異常,從而也避免了類(lèi)的內(nèi)部方法調(diào)用該構(gòu)造函數(shù)。

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

這樣定義之后,該類(lèi)將不會(huì)再被外部實(shí)例化了,否則會(huì)產(chǎn)生編譯錯(cuò)誤。然而這樣的定義帶來(lái)的最直接的負(fù)面影響是該類(lèi)將不能再被子類(lèi)化。

五、避免創(chuàng)建不必要的對(duì)象:

試比較以下兩行代碼在被多次反復(fù)執(zhí)行時(shí)的效率差異:由于String被實(shí)現(xiàn)為不可變對(duì)象,JVM底層將其實(shí)現(xiàn)為常量池,既所有值等于"stringette" 的String對(duì)象實(shí)例共享同一對(duì)象地址,而且還可以保證,對(duì)于所有在同一JVM中運(yùn)行的代碼,只要他們包含相同的字符串字面常量,該對(duì)象就會(huì)被重用。

我們繼續(xù)比較下面的例子,并測(cè)試他們?cè)谶\(yùn)行時(shí)的效率差異:

  1. Boolean b = Boolean.valueOf("true");  
  2. Boolean b = new Boolean("true"); 

前者通過(guò)靜態(tài)工廠(chǎng)方法保證了每次返回的對(duì)象,如果他們都是true或false,那么他們將返回相同的對(duì)象。換句話(huà)說(shuō),valueOf將只會(huì)返回Boolean.TRUE或Boolean.FALSE兩個(gè)靜態(tài)域字段之一。而后面的Boolean構(gòu)造方式,每次都會(huì)構(gòu)造出一個(gè)新的Boolean實(shí)例對(duì)象。這樣在多次調(diào)用后,***種靜態(tài)工廠(chǎng)方法將會(huì)避免大量不必要的Boolean對(duì)象被創(chuàng)建,從而提高了程序的運(yùn)行效率,也降低了垃圾回收的負(fù)擔(dān)。

繼續(xù)比較下面的代碼:

  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.      } 

改進(jìn)后的Person類(lèi)只是在初始化的時(shí)候創(chuàng)建Calender、TimeZone和Date實(shí)例一次,而不是在每次調(diào)用isBabyBoomer方法時(shí)都創(chuàng)建一次他們。如果該方法會(huì)被頻繁調(diào)用,效率的提升將會(huì)極為顯著。

集合框架中的Map接口提供keySet方法,該方法每次都將返回底層原始Map對(duì)象鍵數(shù)據(jù)的視圖,而并不會(huì)為該操作創(chuàng)建一個(gè)Set對(duì)象并填充底層Map所有鍵的對(duì)象拷貝。因此當(dāng)多次調(diào)用該方法并返回不同的Set對(duì)象實(shí)例時(shí),事實(shí)上他們底層指向的將是同一段數(shù)據(jù)的引用。

在該條目中還提到了自動(dòng)裝箱行為給程序運(yùn)行帶來(lái)的性能沖擊,如果可以通過(guò)原始類(lèi)型完成的操作應(yīng)該盡量避免使用裝箱類(lèi)型以及他們之間的交互使用。見(jiàn)下例:

  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.      } 

本例中由于錯(cuò)把long sum定義成Long sum,其效率降低了近10倍,這其中的主要原因便是該錯(cuò)誤導(dǎo)致了2的31次方個(gè)臨時(shí)Long對(duì)象被創(chuàng)建了。

六、消除過(guò)期的對(duì)象引用:

盡管Java不像C/C++那樣需要手工管理內(nèi)存資源,而是通過(guò)更為方便、更為智能的垃圾回收機(jī)制來(lái)幫助開(kāi)發(fā)者清理過(guò)期的資源。即便如此,內(nèi)存泄露問(wèn)題仍然會(huì)發(fā)生在你的程序中,只是和C/C++相比,Java中內(nèi)存泄露更加隱匿,更加難以發(fā)現(xiàn),見(jiàn)如下代碼:

  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.      } 

以上示例代碼,在正常的使用中不會(huì)產(chǎn)生任何邏輯問(wèn)題,然而隨著程序運(yùn)行時(shí)間不斷加長(zhǎng),內(nèi)存泄露造成的副作用將會(huì)慢慢的顯現(xiàn)出來(lái),如磁盤(pán)頁(yè)交換、OutOfMemoryError等。那么內(nèi)存泄露隱藏在程序中的什么地方呢?當(dāng)我們調(diào)用pop方法是,該方法將返回當(dāng)前棧頂?shù)膃lements,同時(shí)將該棧的活動(dòng)區(qū)間(size)減一,然而此時(shí)被彈出的Object仍然保持至少兩處引用,一個(gè)是返回的對(duì)象,另一個(gè)則是該返回對(duì)象在elements數(shù)組中原有棧頂位置的引用。這樣即便外部對(duì)象在使用之后不再引用該Object,那么它仍然不會(huì)被垃圾收集器釋放,久而久之導(dǎo)致了更多類(lèi)似對(duì)象的內(nèi)存泄露。修改方式如下:

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

由于現(xiàn)有的Java垃圾收集器已經(jīng)足夠只能和強(qiáng)大,因此沒(méi)有必要對(duì)所有不在需要的對(duì)象執(zhí)行obj = null的顯示置空操作,這樣反而會(huì)給程序代碼的閱讀帶來(lái)不必要的麻煩,該條目只是推薦在以下3中情形下需要考慮資源手工處理問(wèn)題:

1) 類(lèi)是自己管理內(nèi)存,如例子中的Stack類(lèi)。

2) 使用對(duì)象緩存機(jī)制時(shí),需要考慮被從緩存中換出的對(duì)象,或是長(zhǎng)期不會(huì)被訪(fǎng)問(wèn)到的對(duì)象。

3) 事件監(jiān)聽(tīng)器和相關(guān)回調(diào)。用戶(hù)經(jīng)常會(huì)在需要時(shí)顯示的注冊(cè),然而卻經(jīng)常會(huì)忘記在不用的時(shí)候注銷(xiāo)這些回調(diào)接口實(shí)現(xiàn)類(lèi)。

七、避免使用終結(jié)方法:

任何事情都存在其一定的雙面性或者多面性,對(duì)于C++的開(kāi)發(fā)者,內(nèi)存資源是需要手工分配和釋放的,而對(duì)于Java和C#這種資源托管的開(kāi)發(fā)語(yǔ)言,更多的工作可以交給虛擬機(jī)的垃圾回收器來(lái)完成,由此C++程序得到了運(yùn)行效率,卻失去了安全。在Java的實(shí)際開(kāi)發(fā)中,并非所有的資源都是可以被垃圾回收器自動(dòng)釋放的,如FileInputStream、Graphic2D等class中使用的底層操作系統(tǒng)資源句柄,并不會(huì)隨著對(duì)象實(shí)例被GC回收而被釋放,然而這些資源對(duì)于整個(gè)操作系統(tǒng)而言,都是非常重要的稀缺資源,更多的資源句柄泄露將會(huì)導(dǎo)致整個(gè)操作系統(tǒng)及其運(yùn)行的各種服務(wù)程序的運(yùn)行效率直線(xiàn)下降。那么如何保證系統(tǒng)資源不會(huì)被泄露了?在C++中,由于其資源完全交由開(kāi)發(fā)者自行管理,因此在決定資源何時(shí)釋放的問(wèn)題上有著很優(yōu)雅的支持,C++中的析構(gòu)函數(shù)可以說(shuō)是完成這一工作的天然候選者。任何在棧上聲明的C++對(duì)象,當(dāng)棧退出或者當(dāng)前對(duì)象離開(kāi)其作用域時(shí),該對(duì)象實(shí)例的析構(gòu)函數(shù)都會(huì)被自動(dòng)調(diào)用,因此當(dāng)函數(shù)中有任何異常(Exception)發(fā)生時(shí),在棧被銷(xiāo)毀之前,所有棧對(duì)象的析構(gòu)函數(shù)均會(huì)被自動(dòng)調(diào)用。然而對(duì)于Java的開(kāi)發(fā)者而言,從語(yǔ)言自身視角來(lái)看,Java本身并未提供析構(gòu)函數(shù)這樣的機(jī)制,當(dāng)然這也是和其資源被JVM托管有一定關(guān)系的。

在Java中完成這樣的工作主要是依靠try-finally機(jī)制來(lái)協(xié)助完成的。然而Java中還提供了另外一種被稱(chēng)為finalizer的機(jī)制,使用者僅僅需要重載Object對(duì)象提供的finalize方法,這樣當(dāng)JVM的在進(jìn)行垃圾回收時(shí),就可以自動(dòng)調(diào)用該方法。但是由于對(duì)象何時(shí)被垃圾收集的不確定性,以及finalizer給GC帶來(lái)的性能上的影響,因此并不推薦使用者依靠該方法來(lái)達(dá)到關(guān)鍵資源釋放的目的。比如,有數(shù)千個(gè)圖形句柄都在等待被終結(jié)和回收,可惜的是執(zhí)行終結(jié)方法的線(xiàn)程優(yōu)先級(jí)要低于普通的工作者線(xiàn)程,這樣就會(huì)有大量的圖形句柄資源停留在finalizer的隊(duì)列中而不能被及時(shí)的釋放,最終導(dǎo)致了系統(tǒng)運(yùn)行效率的下降,甚至還會(huì)引發(fā)JVM報(bào)出OutOfMemoryError的錯(cuò)誤。

Java的語(yǔ)言規(guī)范中并沒(méi)有保證該方法會(huì)被及時(shí)的執(zhí)行,甚至都沒(méi)有保證一定會(huì)被執(zhí)行。即便開(kāi)發(fā)者在code中手工調(diào)用了System.gc和System.runFinalization這兩個(gè)方法,這僅僅是提高了finalizer被執(zhí)行的幾率而已。還有一點(diǎn)需要注意的是,被重載的finalize()方法中如果拋出異常,其棧幀軌跡是不會(huì)被打印出來(lái)的。在Java中被推薦的資源釋放方法為,提供顯式的具有良好命名的接口方法,如FileInputStream.close()和Graphic2D.dispose()等。然后使用者在finally區(qū)塊中調(diào)用該方法,見(jiàn)如下代碼:

  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.      } 

那么在實(shí)際的開(kāi)發(fā)中,利用finalizer又能給我們帶來(lái)什么樣的幫助呢?見(jiàn)下例:

  1. public class FinalizeTest {  
  2.          //@Override  
  3.          protected void finalize() throws Throwable {  
  4.              try {  
  5.                  //在調(diào)試過(guò)程中通過(guò)該方法,打印對(duì)象在被收集前的各種狀態(tài),  
  6.  //如判斷是否仍有資源未被釋放,或者是否有狀態(tài)不一致的現(xiàn)象存在。  
  7.  //推薦將該finalize方法設(shè)計(jì)成僅在debug狀態(tài)下可用,而在release  
  8.  //下該方法并不存在,以避免其對(duì)運(yùn)行時(shí)效率的影響。  
  9.                  System.out.println("The current status: " + _myStatus);  
  10.              } finally {  
  11.                  //在finally中對(duì)超類(lèi)finalize方法的調(diào)用是必須的,這樣可以保證整個(gè)class繼承  
  12.  //體系中的finalize鏈都被執(zhí)行。  
  13.                  super.finalize();   
  14.              }  
  15.          }  
  16.      } 

原文鏈接:http://www.cnblogs.com/stephen-liu74/archive/2012/01/13/2228354.html

【編輯推薦】

  1. Java編碼及網(wǎng)絡(luò)傳輸中的編碼問(wèn)題
  2. Rhino 使 JavaScript 應(yīng)用程序更靈動(dòng)
  3. 探討:Java中刪除數(shù)組中重復(fù)元素
  4. Java中帶復(fù)選框的樹(shù)的實(shí)現(xiàn)和應(yīng)用
  5. Java網(wǎng)頁(yè)數(shù)據(jù)采集器實(shí)例教程:數(shù)據(jù)存儲(chǔ)
責(zé)任編輯:林師授 來(lái)源: Stephen_Liu的博客
相關(guān)推薦

2021-11-09 07:59:50

開(kāi)發(fā)

2010-07-05 11:14:32

2010-08-05 13:23:05

NFS協(xié)議

2009-06-18 12:37:26

java編程語(yǔ)言

2010-08-02 17:24:25

NFS目錄

2009-09-28 15:50:53

Hibernate創(chuàng)建持久化Product

2024-06-04 09:02:03

2019-11-11 05:02:05

零信任身份驗(yàn)證網(wǎng)絡(luò)安全

2013-07-21 18:09:21

iOS開(kāi)發(fā)ASIHttpRequ創(chuàng)建和執(zhí)行reques

2009-11-25 09:57:17

PHPPDF

2010-09-10 13:06:27

JavaScript

2024-01-29 10:20:39

Obsidian鏈接

2011-04-15 17:07:13

Java

2009-08-12 13:22:44

Singleton模式

2012-12-03 14:21:02

服務(wù)器群集

2009-04-07 10:45:43

Oracle視圖創(chuàng)建

2022-08-29 08:00:00

容器Portainer工具

2012-02-01 11:20:23

Java線(xiàn)程

2016-08-12 13:22:01

React Nativ環(huán)境搭建創(chuàng)建項(xiàng)目

2010-08-26 11:19:31

CSS樣式表
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 不卡视频一区二区三区 | 久久免费高清视频 | 一区二区三区免费 | 日韩精品免费看 | 玖玖精品| 免费观看黄a一级视频 | 欧美视频在线看 | 国产在线www| 日韩欧美国产精品一区二区三区 | 精精国产视频 | 在线视频91 | 亚洲va在线va天堂va狼色在线 | 日韩成人av在线 | 青青草原精品99久久精品66 | 91精品国产91久久久久久吃药 | 中文字幕第十页 | 最新国产精品 | 欧美二区在线 | 成人av色| 国产羞羞视频在线观看 | 极品的亚洲 | 久久久涩| 一区精品视频在线观看 | 国产精品久久久亚洲 | 午夜精品久久久久久久99黑人 | 亚洲人成人一区二区在线观看 | 国产精品久久久久久吹潮 | 久久久久久久香蕉 | 国产午夜精品视频 | 97人人爱 | 日韩中文字幕高清 | 99在线观看视频 | 最新毛片网站 | 在线精品亚洲欧美日韩国产 | 另类专区亚洲 | 亚洲视频二区 | 精品视频在线播放 | 亚洲视频在线看 | 亚洲精品日韩综合观看成人91 | 黄色大片免费观看 | 色播99|