工欲善其事必先利其器! java
再快不能快基礎,再爛不能爛語言! 程序員
"咱們之因此將天然界分解,組織成各類概念,並按其含義分類,主要是由於咱們是整個口語交流社會共同遵照的協定的參與者,這個協定以語言的形式固定下來......除非同意這個協定中規定的有關語言信息的組織和分類,不然咱們根本沒法交談。———Benjamin Lee Whorf( 1897~1941 )"
建立抽象數據類型(類)是面向對象程序設計的基本概念之一。編程
抽象數據的運行方式與內置(built-in)類型幾乎徹底一致:你能夠建立某一類型的變量(按 照面向對象的說法,稱其爲對象或者實例),而後操做這些變量(稱其爲發送消息或者請 求;發送消息,對象就知道要作什麼)。數組
每個對象都屬於定義了特性和行爲的某個特定的類。安全
由於類描述了具備相同特性(數據元素)和行爲(功能)的對象集合,因此一個類實際上就是一個數據類型。bash
接口肯定了對某一特定對象所能發出的請求。併發
高內聚低耦合函數
內聚: 是從功能角度來度量模塊內的聯繫,一個好的內聚模塊應當剛好作一件事。它描述 的是模塊內的功能聯繫;工具
耦合: 是軟件結構中各模塊之間相互鏈接的一種度量,耦合強弱取決於模塊間接口的複雜 程度、進入或訪問一個模塊的點以及經過接口的數據。佈局
高內聚低耦合,是軟件工程中的概念,是判斷設計好壞的標準,主要是面向對象的設 計,主要是看類的內聚性是否高,耦合度是否低。
耦合性: 也稱塊間聯繫。指軟件系統結構中各模塊間相互聯繫緊密程度的一種度量。模 塊之間聯繫越緊密,其耦合性就越強,模塊的獨立性則越差。模塊間耦合高低取決於模 塊間接口的複雜性、調用的方式及傳遞的信息
內聚性: 又稱塊內聯繫。指模塊的功能強度的度量,即一個模塊內部各個元素彼此結合 的緊密程度的度量。若一個模塊內各元素(語名之間、程序段之間)聯繫的越緊密,則 它的內聚性就越高。
所謂高內聚是指一個軟件模塊是由相關性很強的代碼組成,只負責一項任務,也就是常 說的單一責任原則。
耦合: 一個軟件結構內不一樣模塊之間互連程度的度量。
對於低耦合,粗淺的理解是:一個完整的系統,模塊與模塊之間,儘量的使其獨立存 在。 也就是說,讓每一個模塊,儘量的獨立完成某個特定的子功能。模塊與模塊之間的 接口,儘可能的少而簡單。若是某兩個模塊間的關係比較複雜的話,最好首先考慮進一步 的模塊劃分。這樣有利於修改和組合。
將對象看做是服務提供者還有一個附帶的好處:它有助於提升對象的內聚性。高內聚是 軟件設計的基本質量要求之一:這意味着一個軟件構造(例如一個對象,固然它也有可能 是指一個方法或一個對象庫)的各個方面「組合」得很好。人們在設計對象時所面臨的一個 問題是,將過多的功能都塞在一個對象中。
在良好的面向對象設計中,每一個對象均可以很好地完成一項任務,可是它並不試圖作更多的事情。
將對象做爲服務提供者看待是一件偉大的簡化工具,這不只在設計過程當中很是有用,而 且當其餘人試圖理解你的代碼或重用某個對象時,若是他們看出了這個對象所能提供的 服務的價值,它會使調整對象以適應其設計的過程變得簡單得多。
代碼複用是面向對象程序設計語言鎖提供的最了不得的優勢之一。
新的類型能夠由任意數量,任意類型的其餘對象以任意能夠實現新的類中想要的功能的方式組成。
由於是在使用現有的類合成新的其餘對象的類,因此這種概念被稱爲組合(composition),若是組合是動態發生的,那麼它一般被稱爲聚合(aggregation)。
在創建新類時,應該首先考慮組合,由於它更加簡單靈活。 若是採用這種方式,設計會變得更加清晰,一旦有了一些經驗以後,便可以看出必須使用繼承的場合了。
當源類(被稱爲基類,超類或父類)發生變更時,被修改的「副本」(被稱爲導出類,繼承類或者子類)也會反應出變更。
能夠建立一個基類來表示系統中某些對象的核心概念,從基類型中導出其餘類型,來表示此核心能夠被實現的各類不一樣方式。
當繼承現有類型時,也就建立了新的類型。 這個新的類型不只包括如今類型的全部成員(儘管private成員被隱藏起來,而且不可訪問),並且更重要的是它複製了基類的接口。也就是說,全部能夠發送給基類對象的消息同時也能夠發送到導出類對象。
經過繼承而產生的類型等價性是理解面向對象程序設計方法內涵的重要門檻!
有兩種方法可使基類與導出類產生差別
繼承應該只覆蓋基類的方法(而不添加在基類中沒有的新方法),在某種意義上,這是一種處理繼承的理想方式。咱們常常將這種狀況下的基類與導出類之間的關係稱爲is-a(是一個)關係。
有時必須在導出類型添加新的接口元素,這樣也就擴展了接口。這種狀況咱們能夠描述爲is-like-a(像是一個)關係。
經過導出新的子類型而輕鬆擴展設計的能力是對改動進行封裝的基本方式之一。
這種能力能夠極大地改善咱們的設計,同時也下降軟件維護的代價。
面向對象程序設計的最重要妙訣:編譯器不可能產生傳統意義上的函數調用。 一個非面向對象編程的編譯器產生的函數調用會引發所謂的前期綁定 ,這個術語你可能之前從未據說過,可能從未想過函數調用的其餘方式。這麼作意味着編譯器將產生對一個具體函數名字的調用,而運行時將這個調用解析到將要被執行的代碼的絕對地址。然而在OOP(面向對象編程)中,程序直到運行時纔可以肯定代碼的地址,因此當消息發送到一個泛化對象時,必須採用其餘的機制。
當向對象發送消息時,被調用的代碼直到運行時才能肯定。 編譯器確保被調用方法的存在,並對調用參數和返回值執行類型檢查(沒法提供此類保證的語言被稱爲是弱類型的),可是並不知道將被執行的確切代碼。
在java中,動態綁定是默認行爲,不須要添加額外的關鍵字來實現多態。
把將被導出類看作是它的基類的過程稱爲向上轉型。
轉型這個名稱的靈感來自於模型鑄造的塑模動做;而向上(up)這個詞來源於繼承圖的典型佈局方式;一般基類在頂部,而導出類在其下部散開。所以,轉型爲一個基類就是在繼承圖中向上移動,即「向上轉型」。
正是由於多態才使得事情老是可以被正確處理。編譯器和運行系統會處理相關的細節,你須要立刻知道的只是事情會發生,更重要的是怎樣經過它來設計。當向一個對象發送消息時,即便涉及向上轉型,該對象也知道要執行什麼樣的正確行爲。
容器(也稱爲集合,不過java類庫以不一樣的含義使用「集合」這個術語,因此本書使用「容器」這個詞),在任何須要時均可擴充本身以容納你置於其中的全部東西。所以不須要知道未來會把多少個對象置於容器中,只須要建立一個容器對象,而後讓它處理全部細節。
Java容器
使用時仍是要對容器有所選擇,緣由以下:
咱們能夠在一開始使用LinkedList構建程序,而在優化系統性能時改用ArrayList。接口List所帶來的抽象,把在容器之間進行轉換時對代碼產生的影響下降到最小限度。
參數化類型(範型) 在JavaSE5以前,容器存儲的對象都只具備Java中的通用類型:Object。
單根繼承結構意味着全部東西都是Object類型,因此能夠存儲Object的容器能夠存儲任何東西。這使得容器很容易被複用。
向上轉型是安全的,例如Circle是一種Shape類型;可是不知道某個Object是Circle仍是Shape,因此除非確切知道所要處理的對象的類型,不然向下轉型幾乎是不安全的。
若是向下轉型錯誤的話,就會獲得被稱爲異常的運行時錯誤。
參數化類型(範型)就是一個編譯器能夠自動定製用於特定類型上的類。 例如,經過使用參數化類型,編譯器能夠定製一個只接納和取出Shape對象的容器。
爲了利用泛型的優勢,不少標準類庫構建都已經進行了修改。就像咱們將要看到的那樣,範型對本書中的許多代碼都產生了重要的影響。
在使用對象時,最關鍵的問題之一即是它們的生成和銷燬方式。
每一個對象爲了生存都須要資源,尤爲是內存。當咱們再也不須要一個對象時,它必須被清理掉。使其佔有的資源能夠被釋放和重用。
爲了追求最大的執行速度,對象的存儲空間和生命週期能夠在編寫程序時肯定。
這能夠經過將對象置於堆棧(它們有時被稱爲自動變量)或限域變量或靜態存儲區域內來實現。
這種方式將存儲空間分配和釋放置於優先考慮的位置,某些狀況下這樣的控制很是有價值。可是,也犧牲了靈活性,由於必須在編寫程序時知道對象確切的數量,生命週期和類型。
第二種方式是在被稱爲堆(heap)的內存池中動態地建立對象。
在這種方式中,直到運行時才知道須要多少對象,它們的生命週期如何,以及它們的具體類型是什麼。
若是須要一個新對象,能夠在須要的時刻直接在堆中建立。由於存儲空間是在運行時被動態管理的,因此須要大量的時間在堆中分配存儲空間,這可能要遠遠大於在堆棧中建立存儲空間的時間。
建立堆棧存儲空間的時間依賴於存儲機制的設計
動態方式有這樣一個通常性的邏輯假設:對象趨向於變得複雜。因此查找和釋放存儲空間的開銷不會對對象的建立形成重大沖擊。動態方式所帶來的更大的靈活性正是解決通常化編程問題的要點所在。
java徹底採用動態內存分配方式。每當想要建立新對象時,就要使用new關鍵字來構建此對象的動態實例。
還有一個議題,就是生命週期。對於容許在堆棧上建立對象的語言,編譯器能夠肯定對象存活的時間,並能夠自動銷燬它。
java提供了被稱爲「垃圾回收器」的機制,它能夠自動發現對象什麼時候再也不被使用,並繼而銷燬它。
垃圾回收器很是有用,由於它減小了所必須考慮的議題和必須編寫的代碼。更重要的是,垃圾回收器提供了更高層的保障,能夠避免暗藏的內存泄漏問題。
java的垃圾回收器被設計用來處理內存釋放問題(儘管它不包括清理對象的其餘方面)。垃圾回收器「知道」對象什麼時候再也不被使用,並自動釋放對象佔用的內存。
"若是咱們說另外一種不一樣的語言,那麼咱們就會發覺一個有些不一樣的世界。——Luduing Wittgerstein( 1889~1951 )"
java中,一切都被視爲對象,所以可採用單一的語法。儘管一切都看做是對象,但操做的標識符其實是對象的一個「引用」。
例:遙控器(引用)操做電視機(對象)。
寄存器: 最快的存儲區,但因爲寄存器的數量極其有限,因此根據需求進行分配。你不能直接控制,在程序中也感受不到寄存器存在的任何跡象。
堆棧: 位於通用RAM(隨機訪問存儲器)中。堆棧指針若往下移則分配新內存,若往上移動則釋放內存。
堆棧:就是STACK。其實是隻有一個出入口的隊列,即後進先出(First In Last Out),先分配的內存一定後釋放。通常由系統自動分配,存放函數的參數值,局部變量等,自動清除。
堆棧是每一個函數進入的時候分一小塊,函數返回的時候就釋放了
局部變量放在堆棧中,因此函數返回,局部變量就全沒了。
堆: 一種通用的內存池(位於RAM區),用來存放全部的Java對象。
當須要一個對象時,只需用new寫一行簡單的代碼,當執行這行代碼時,會自動在堆裏進行存儲分配。
用堆進行存儲分配和清理可能比用堆棧進行存儲分配須要更多的時間。
常量存儲: 常量值一般是直接存放在程序代碼內部。
非RAM存儲: 若是數據徹底存活於程序以外,那麼它能夠不受程序的任何控制,在程序沒有運行也能夠存在。例如:流對象和持久化對象。
class Banana { void peel(int i) {/*...*/} }
public class BananaPeel{
public static void main(String[] args) {
Banana a = new Banana();
Banana b = new Banana();
a.peel(1);
b.peel(2);
}
}
複製代碼
【問題】peel()如何知道是被a仍是被b調用的呢?
【答】:爲了能簡便面向對象的語法來編寫代碼,編譯器暗自把「操做對象的引用」做爲第一個參數傳遞給peel(),
a.peel(a,1);這是內部表現形式。
複製代碼
java中的垃圾回收器負責回收無用對象佔據的內存資源。
垃圾回收器只知道釋放由new分配的內存。
finalize():在垃圾回收器準備好釋放回收對象佔用的存儲空間以前調用的方法,能夠自定義作一些清理工做。
【問題】finalize()和析構函數的區別:
析構函數(C++中銷燬對象必須用這個函數):「析構」函數與構造函數相反,當對象結束其生命週期,
如對象所在的函數已調用完畢時,系統自動執行析構函數。析構函數每每用來作「清理善後」的工做。
複製代碼
Java對象並不是老是被垃圾回收:
在java中,只要程序沒有瀕臨存儲用完的那一刻,對象佔用的空間就總也得不到釋放。
使用垃圾回收器的惟一緣由是爲了回收程序再也不使用的內存。
finalize()的用途之——釋放空間:
當代碼中使用「本地方法」的狀況下,好比調用C的malloc()函數系列來分配存儲空間時,須要在finalize中調用free()來釋放內存空間。
注:本地方法是一種在java中調用非java代碼,通常用「native」關鍵字修飾。
finalize()的用途之——終結條件驗證:
在對象資源被釋放以前驗證對象的某個狀態,將驗證內容放在finalize()的方法中,能夠避免一些因爲代碼書寫問題致使的缺陷。
注:【Effective Java】中「避免使用終結方法和清楚方法」一節中提到:終結方法(finalize)一般是不可預測的,也是很危險的,通常狀況下是沒必要要的。使用終結方法會致使行爲不穩定,性能下降,以及可移植性問題,根據經驗,應該避免使用終結方法。
垃圾回收器如何工做
在堆上分配對象代價是十分高昂的,所以java中全部對象的分配方式也是很是高昂的。然而垃圾回收器對於提升對象的建立速度,卻具備明顯的效果。
垃圾回收器的實現:
垃圾回收器依據的思想是:對任何「活」的對象,必定能最終追溯到其存在在堆棧或靜態存儲區之中的引用。
引用計數器: 是一種簡單但速度很慢的垃圾回收技術。
每一個對象都含有一個引用計數器,當有引用鏈接至對象時,引用計數加1。當引用離開做用域或設置爲null的時候,引用計數器減1。垃圾回收器會在含有所有對象的列表上便利,當發現某個對象的引用計數爲0時,就釋放其佔用的空間。但循環引用會致使「對象應該被回收,計數卻不爲0」。
引用計數未被應用於任何一種java虛擬機實現中。
中止,複製:
先暫停程序的運行(不屬於後臺回收模式),而後將全部存活的對象從當前堆複製到另外一個堆,沒有被複制的所有都是垃圾。當把對象從一處搬到另外一處時,全部指向它的那些引用都必須修正了。位於堆或靜態存儲的引用能夠直接被修改,但還有其餘指向的引用,在遍歷的過程才能被找到。
這種方式,效率會很低。一方面須要在兩個分離的堆之間來回倒騰;另外一方面是程序穩定後,可能只會產生少許垃圾,甚至沒有垃圾,但仍然會將全部的內存自一處複製到另外一處。
垃圾回收動做不是在後臺進行的,垃圾回收動做發生時,程序會被暫停。
「中止-複製」要求在釋放舊的對象以前,必須把全部存活的對象從舊堆裏複製到新堆。這將致使大量內存複製行爲。
標記,清掃
從堆棧和靜態存儲區出發,遍歷全部的引用,進而找出全部存活的對象。每當找到一個存活對象,就會給對象設一個標記,但這時不回收任何對象。只有所有標記工做完成,纔開始清理。沒有標記的將被釋放,有標記纔會進行復制動做。必須在程序暫停的狀況下才能進行。
「自適應的,分代的,中止-複製,標記-清掃」式垃圾回收器
類中的變量初始化在構造器以前
不管建立多少個對象,靜態數據都只佔一份存儲區域。
static關鍵不能應用於局部變量,只能做用於域。
若是是一個靜態的基本類型域,且沒有初始化,那麼它就會得到基本類型的標準初值。
若是它是一個對象引用,那麼它的默認初始值就是null。
靜態存儲初始化時間:
類中的靜態成員都會隨着類的加載而加載,好比建立對象時,或者是被引用時!
類中的靜態成員都會隨着類的加載而加載,好比建立對象時,或者是被引用時!
類中的靜態成員都會隨着類的加載而加載,好比建立對象時,或者是被引用時!
初始化的順序是先靜態對象,然後「非靜態」對象。
對象的建立過程
【推薦篇】- 書籍內容整理筆記 | 連接地址 |
---|---|
【推薦】【Java編程思想】【筆記】 | juejin.im/post/5dbb7a… |
【推薦】【Java核心技術 卷Ⅰ】【筆記】 | juejin.im/post/5dbb7b… |
後期持續更新中。。。。。。
如有書寫錯誤的地方,歡迎留言,但願咱們能夠一塊兒進步,一塊兒加油!😜😜