熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Java核心技術 >> 正文

對象的創建和存在時間

2013-11-23 18:44:09  來源: Java核心技術 

  從技術角度說OOP(面向對象程序設計)只是涉及抽象的數據類型繼承以及多形性但另一些問題也可能顯得非常重要本節將就這些問題進行探討
  最重要的問題之一是對象的創建及破壞方式對象需要的數據位於哪兒如何控制對象的存在時間呢?針對這個問題解決的方案是各異其趣的C++認為程序的執行效率是最重要的一個問題所以它允許程序員作出選擇為獲得最快的運行速度存儲以及存在時間可在編寫程序時決定只需將對象放置在堆棧(有時也叫作自動或定域變量)或者靜態存儲區域即可這樣便為存儲空間的分配和釋放提供了一個優先級某些情況下這種優先級的控制是非常有價值的然而我們同時也犧牲了靈活性因為在編寫程序時必須知道對象的准確的數量存在時間以及類型如果要解決的是一個較常規的問題如計算機輔助設計倉儲管理或者空中交通控制這一方法就顯得太局限了
  第二個方法是在一個內存池中動態創建對象該內存池亦叫或者內存堆若采用這種方式除非進入運行期否則根本不知道到底需要多少個對象也不知道它們的存在時間有多長以及准確的類型是什麼這些參數都在程序正式運行時才決定的若需一個新對象只需在需要它的時候在內存堆裡簡單地創建它即可由於存儲空間的管理是運行期間動態進行的所以在內存堆裡分配存儲空間的時間比在堆棧裡創建的時間長得多(在堆棧裡創建存儲空間一般只需要一個簡單的指令將堆棧指針向下或向下移動即可)由於動態創建方法使對象本來就傾向於復雜所以查找存儲空間以及釋放它所需的額外開銷不會為對象的創建造成明顯的影響除此以外更大的靈活性對於常規編程問題的解決是至關重要的
  C++允許我們決定是在寫程序時創建對象還是在運行期間創建這種控制方法更加靈活大家或許認為既然它如此靈活那麼無論如何都應在內存堆裡創建對象而不是在堆棧中創建但還要考慮另外一個問題亦即對象的存在時間或者生存時間(Lifetime)若在堆棧或者靜態存儲空間裡創建一個對象編譯器會判斷對象的持續時間有多長到時會自動破壞或者清除程序員可用兩種方法來破壞一個對象用程序化的方式決定何時破壞對象或者利用由運行環境提供的一種垃圾收集器特性自動尋找那些不再使用的對象並將其清除當然垃圾收集器顯得方便得多但要求所有應用程序都必須容忍垃圾收集器的存在並能默許隨垃圾收集帶來的額外開銷但這並不符合C++語言的設計宗旨所以未能包括到C++裡但Java確實提供了一個垃圾收集器(Smalltalk也有這樣的設計盡管Delphi默認為沒有垃圾收集器但可選擇安裝而C++亦可使用一些由其他公司開發的垃圾收集產品)
  本節剩下的部分將討論操縱對象時要考慮的另一些因素
   集合與繼承器
  針對一個特定問題的解決如果事先不知道需要多少個對象或者它們的持續時間有多長那麼也不知道如何保存那些對象既然如此怎樣才能知道那些對象要求多少空間呢?事先上根本無法提前知道除非進入運行期
  在面向對象的設計中大多數問題的解決辦法似乎都有些輕率——只是簡單地創建另一種類型的對象用於解決特定問題的新型對象容納了指向其他對象的句柄當然也可以用數組來做同樣的事情那是大多數語言都具有的一種功能但不能只看到這一點這種新對象通常叫作集合(亦叫作一個容器但AWT在不同的場合應用了這個術語所以本書將一直沿用集合的稱呼在需要的時候集合會自動擴充自己以便適應我們在其中置入的任何東西所以我們事先不必知道要在一個集合裡容下多少東西只需創建一個集合以後的工作讓它自己負責好了
  幸運的是設計優良的OOP語言都配套提供了一系列集合在C++中它們是以標准模板庫(STL)的形式提供的Object Pascal用自己的可視組件庫(VCL)提供集合Smalltalk提供了一套非常完整的集合而Java也用自己的標准庫提供了集合在某些庫中一個常規集合便可滿足人們的大多數要求而在另一些庫中(特別是C++的庫)則面向不同的需求提供了不同類型的集合例如可以用一個矢量統一對所有元素的訪問方式一個鏈接列表則用於保證所有元素的插入統一所以我們能根據自己的需要選擇適當的類型其中包括集隊列散列表堆棧等等
  所有集合都提供了相應的讀寫功能將某樣東西置入集合時采用的方式是十分明顯的有一個叫作(Push)添加(Add)或其他類似名字的函數用於做這件事情但將數據從集合中取出的時候方式卻並不總是那麼明顯如果是一個數組形式的實體比如一個矢量(Vector)那麼也許能用索引運算符或函數但在許多情況下這樣做往往會無功而返此外單選定函數的功能是非常有限的如果想對集合中的一系列元素進行操縱或比較而不是僅僅面向一個這時又該怎麼辦呢?
  辦法就是使用一個繼續器(Iterator)它屬於一種對象負責選擇集合內的元素並把它們提供給繼承器的用戶作為一個類它也提供了一級抽象利用這一級抽象可將集合細節與用於訪問那個集合的代碼隔離開通過繼承器的作用集合被抽象成一個簡單的序列繼承器允許我們遍歷那個序列同時毋需關心基礎結構是什麼——換言之不管它是一個矢量一個鏈接列表一個堆棧還是其他什麼東西這樣一來我們就可以靈活地改變基礎數據不會對程序裡的代碼造成干擾Java最開始(在版中)提供的是一個標准繼承器名為Enumeration(枚舉)為它的所有集合類提供服務Java 新增一個更復雜的集合庫其中包含了一個名為Iterator的繼承器可以做比老式的Enumeration更多的事情
  從設計角度出發我們需要的是一個全功能的序列通過對它的操縱應該能解決自己的問題如果一種類型的序列即可滿足我們的所有要求那麼完全沒有必要再換用不同的類型有兩方面的原因促使我們需要對集合作出選擇首先集合提供了不同的接口類型以及外部行為堆棧的接口與行為與隊列的不同而隊列的接口與行為又與一個集(Set)或列表的不同利用這個特征我們解決問題時便有更大的靈活性
  其次不同的集合在進行特定操作時往往有不同的效率最好的例子便是矢量(Vector)和列表(List)的區別它們都屬於簡單的序列擁有完全一致的接口和外部行為但在執行一些特定的任務時需要的開銷卻是完全不同的對矢量內的元素進行的隨機訪問(存取)是一種常時操作無論我們選擇的選擇是什麼需要的時間量都是相同的但在一個鏈接列表中若想到處移動並隨機挑選一個元素就需付出慘重的代價而且假設某個元素位於列表較遠的地方找到它所需的時間也會長許多但在另一方面如果想在序列中部插入一個元素用列表就比用矢量劃算得多這些以及其他操作都有不同的執行效率具體取決於序列的基礎結構是什麼在設計階段我們可以先從一個列表開始最後調整性能的時候再根據情況把它換成矢量由於抽象是通過繼承器進行的所以能在兩者方便地切換對代碼的影響則顯得微不足道
  最後記住集合只是一個用來放置對象的儲藏所如果那個儲藏所能滿足我們的所有需要就完全沒必要關心它具體是如何實現的(這是大多數類型對象的一個基本概念)如果在一個編程環境中工作它由於其他因素(比如在Windows下運行或者由垃圾收集器帶來了開銷)產生了內在的開銷那麼矢量和鏈接列表之間在系統開銷上的差異就或許不是一個大問題我們可能只需要一種類型的序列甚至可以想象有一個完美的集合抽象它能根據自己的使用方式自動改變基層的實現方式
   單根結構
  在面向對象的程序設計中由於C++的引入而顯得尤為突出的一個問題是所有類最終是否都應從單獨一個基礎類繼承在Java中(與其他幾乎所有OOP語言一樣)對這個問題的答案都是肯定的而且這個終級基礎類的名字很簡單就是一個Object這種單根結構具有許多方面的優點
  單根結構中的所有對象都有一個通用接口所以它們最終都屬於相同的類型另一種方案(就象C++那樣)是我們不能保證所有東西都屬於相同的基本類型從向後兼容的角度看這一方案可與C模型更好地配合而且可以認為它的限制更少一些但假期我們想進行純粹的面向對象編程那麼必須構建自己的結構以期獲得與內建到其他OOP語言裡的同樣的便利需添加我們要用到的各種新類庫還要使用另一些不兼容的接口理所當然地這也需要付出額外的精力使新接口與自己的設計方案配合(可能還需要多重繼承)為得到C++額外的靈活性付出這樣的代價值得嗎?當然如果真的需要——如果早已是C專家如果對C有難捨的情結——那麼就真的很值得但假如你是一名新手首次接觸這類設計象Java那樣的替換方案也許會更省事一些
  單根結構中的所有對象(比如所有Java對象)都可以保證擁有一些特定的功能在自己的系統中我們知道對每個對象都能進行一些基本操作一個單根結構加上所有對象都在內存堆中創建可以極大簡化參數的傳遞(這在C++裡是一個復雜的概念)
  利用單根結構我們可以更方便地實現一個垃圾收集器與此有關的必要支持可安裝於基礎類中而垃圾收集器可將適當的消息發給系統內的任何對象如果沒有這種單根結構而且系統通過一個句柄來操縱對象那麼實現垃圾收集器的途徑會有很大的不同而且會面臨許多障礙
  由於運行期的類型信息肯定存在於所有對象中所以永遠不會遇到判斷不出一個對象的類型的情況這對系統級的操作來說顯得特別重要比如違例控制而且也能在程序設計時獲得更大的靈活性
  但大家也可能產生疑問既然你把好處說得這麼天花亂墜為什麼C++沒有采用單根結構呢?事實上這是早
From:http://tw.wingwit.com/Article/program/Java/hx/201311/25640.html
  • 上一篇文章:

  • 下一篇文章:
  • 推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.