1.抽象程序員
面向過程的語言,它們所做的主要抽象仍要求在解決問題時要基於計算機結構,而不是基於所要解決的問題的結構來考慮。程序員還需創建起機器模型(如計算機)與實際待解決問題的模型之間的關聯,耗時費力,難以維護。編程
而面向對象,只針對待解問題建模。把需求想象爲一個空間,而對象則是裏面的物件。併發
咱們將問題空間中的元素及其在解空間中的表示稱爲「對象」。這種思想的實質是,程序能夠經過添加新類型的對象使自身適用於某個特定問題。所以,當你在閱讀描述解決方案的代碼的同時,也是在閱讀問題的表述。ide
Alan Klay總結了面向對象程序設計的五個特性,這些特性也表現了面向對象程序設計的方式:函數
1)萬物皆爲對象。理論上講,你能夠抽取待求解問題的任何概念化構件(狗,建築物,服務等),將其表示爲程序中的對象。工具
2)程序是對象的集合,它們經過發送消息來告之彼此所要作的。要想請求一個對象,就必須對該對象發送一條消息。更具體地說,能夠把消息想象爲對某個特定對象的方法的調用請求。spa
3)每一個對象都有本身的由其餘對象所構成的存儲。換句話說,能夠經過建立包含現有對象的包的方式來建立新類型的對象。(繼承?)線程
4)每一個對象都擁有其類型。每一個對象都是某個類(class)的一個實例(instance)。設計
5)某一特定類型的全部對象均可以接收一樣的消息。由於「圓形「類型的對象同時也是」幾何形「類型的對象,因此一個」圓形「對象一定可以接受發送給」幾何形「對象的消息。這意味着能夠編寫與」幾何形「交互並自動處理全部與幾何形性質相關的實物的代碼。(這種可替代性是多態?)指針
2.每一個對象都有一個接口
面向對象方法並非僅侷限於構建仿真程序。不管你是否同意如下觀點,即任何程序都是你所設計的系統的一種仿真,面向對象技術的應用確實能夠將大量的問題很容易地降解爲一個簡單的解決方案。(這一段還沒有明白)
事實上,面向對象程序設計的挑戰之一,就是在問題空間的元素和解空間的對象之間建立一對一的映射。
必須有某種方式產生對對象的請求,使對象完成各類任務,這樣才能得到有用的對象。這些請求由對象的接口(interface)所定義,決定接口的即是類型。
向某個對象」發送消息「(產生請求),這個對象便知道此消息的目的,而後執行對應的程序代碼。
3.每一個對象都提供服務
當試圖開發或理解一個程序設計時,最好的方法之一就是將對象想象爲」服務提供者「。程序自己將向用戶提供服務,它將經過調用其餘對象提供的服務來實現這一目的。你的目標就是去建立(或者最好是在現有代碼庫中尋找)可以提供理想的服務來解決問題的一系列對象。
將對象看做是服務提供者還有一個附帶的好處:它有助於提升對象的內聚性。
4.被隱藏的具體實現
訪問控制的第一個存在緣由就是讓客戶端程序員沒法觸及他們不該該觸及的部分----這些部分對數據類型的內部操做來講是必需的,但並非用戶解決特定問題所需的接口的一部分。
訪問控制的第二個存在緣由就是容許庫設計者能夠改變類內部的工做方式而不用擔憂會影響到客戶端程序員。
5.複用具體實現
新的類能夠由任意數量,任意類型的其餘對象以任意能夠實現新的類中想要的功能的方式所組成。由於是在使用現有的類合成新的類,因此這種概念被稱爲組合(Composition),若是組合是動態發生的,那麼它一般被稱爲聚合(aggregation)。組合常常被視爲"has-a"(擁有)關係。
實際上,在創建新類時,應該首先考慮組合,由於它更加簡單靈活。若是採用這種方式,設計會變得更加清晰。
6.繼承
當繼承現有類型時,也就創造了新的類型。這個新的類型不只包括現有類型的全部成員(儘管private成員被隱藏了起來,而且不可訪問),並且最重要的是它複製了基類的接口。也就是說,全部能夠發送給基類對象的消息同時也能夠發送給導出類對象。
有兩種方法可使基類與導出類產生差別。第一種方法很是直接:直接在導出類中添加新方法。可是,應該仔細考慮是否存在基類也須要這些額外方法的可能性。
第二種也是更重要的一種使導出類和基類之間產生差別的方法是改變現有基類的方法的行爲,這被稱之爲覆蓋(overriding)那個方法。
若是繼承只覆蓋基類的方法(而並不添加在基類中沒有的新方法),就意味着導出類和基類是徹底相同的類型,由於它們具備徹底相同的接口。結果能夠用一個導出類對象徹底替代一個基類對象。這能夠被視爲純粹替代,一般稱之爲替代原則。咱們常常將這種狀況下的基類與導出類的關係稱爲is-a(是一個)關係。
若是在導出類中添加新的接口元素,這個新的類型仍然能夠替代基類,可是這種替代並不完美,由於基類沒法訪問新添加的方法,這種狀況咱們能夠描述爲is-like-a(像是一個)關係。
7.伴隨多態的可互換對象
在試圖將導出類型的對象看成其泛化基類型對象來看待時(把圓形看做是幾何形,把自行車看做是交通工具等等),仍然存在一個問題。若是某個方法要讓泛化幾何形狀繪製本身,讓泛化交通工具行駛,或者讓泛化的鳥類移動,那麼編譯器在編譯時是不可能知道應該執行哪一段代碼的。
這就是關鍵所在:當發送這樣的消息時,程序員並不想知道哪一段代碼將被執行;繪圖方法能夠被等同地應用於圓形,正方形,三角形,而對象會依據自身的具體類型來執行恰當的代碼。
若是不須要知道哪一段代碼會被執行,那麼當添加新的子類型時,不須要更改調用它的方法,它就可以執行不一樣的代碼。(我的理解:使用父類對象引用接口方法,動態綁定會本身調用最適合的子類方法)。所以,編譯器沒法精確地瞭解哪一段代碼將會被執行。
這個問題的答案,也是面向對象程序設計的最重要的妙訣:編譯器不可能產生傳統意義上的函數調用(編譯器將產生對一個具體函數名字的調用,而運行時將這個調用解析到將要被執行的代碼的絕對地址)。在OOP中,程序直到運行時纔可以肯定代碼的地址,因此當消息發送到一個泛化對象時,必須採用其餘的機制。
爲了解決這個問題,面向對象程序設計語言使用了後期綁定(動態綁定)的概念。當向對象發送消息時,被調用的代碼直到運行時才能肯定。編譯器確保被調用方法的存在,並對調用參數和返回值執行類型檢查(沒法提供此類保證的語言被稱爲是弱類型的),可是並不知道將被執行的確切代碼。
爲了執行後期綁定,Java使用一小段特殊的代碼來替代絕對地址調用。這段代碼使用在對象中存儲的信息來計算方法體的地址(反射?)。
多態小Demo:
public class PolymorphismDemo { public static void doSomething(Person person){ person.talk(); } public static void main(String[] args) { Person person=new Person(); Fans dk=new Fans(); Spiderman peter=new Spiderman(); PolymorphismDemo.doSomething(dk); PolymorphismDemo.doSomething(peter); PolymorphismDemo.doSomething(person); } } class Person{ public void talk(){ System.out.println("nm"); } } class Fans extends Person{ @Override public void talk() { System.out.println("嚶嚶嚶"); } } class Spiderman extends Person{ @Override public void talk() { System.out.println("jiujiujiu"); } }
把將導出類看作是它的基類的過程稱爲向上轉型(upcasting)。一個面向對象程序確定會在某處包含向上轉型,由於這正是將本身從必須知道確切類型中解放出來的關鍵。
8.單根繼承結構
單根繼承結構保證全部對象都具有某些功能。所以你知道,在你的系統中你能夠在每一個對象上執行某些基本操做。全部對象均可以很容易地在堆上建立,而參數傳遞也獲得了極大的簡化。
單根繼承結構使垃圾回收器的實現變得容易得多。因爲全部對象都保證具備其類型信息,所以不會因沒法肯定對象的類型而陷入僵局。這對於系統級操做(如異常處理)顯得尤爲重要,而且給編程帶來了更大的靈活性。
9.容器
一般來講,若是不知道在解決某個特定問題時須要多少個對象,或者它們將存活多久,那麼就不可能知道爲什麼存儲這些對象。如何才能知道須要多少空間來建立這些對象呢?答案是你不可能知道,由於這類信息只有在運行時才能得到。(這一段還沒有明白)。
對於面向對象設計中的大多數問題而言,這個問題彷佛過於輕率:建立另外一種對象類型。這種新的對象類型持有對其餘對象的引用。這個一般被稱爲容器(也稱爲集合)。
在任何須要時均可擴充本身以容納你置於其中的全部東西。所以不須要知道未來會把多少個對象置於容器中,只須要建立一個容器對象,而後讓它處理全部細節。
從設計的觀點來看,真正須要的只是一個能夠被操做,從而解決問題的序列。若是單一類型的容器能夠知足全部須要,那麼就沒有理由設計不一樣種類的序列了。然而仍是須要對容器有所選擇,這有兩個緣由:
第一,不一樣容器提供了不一樣類型的接口和行爲。
第二,不一樣的容器對於某些操做具備不一樣的效率。
向下轉型和運行時的檢查須要額外的程序運行時間,也須要程序員付出更多的心血。那麼建立這樣的容器,它知道本身所保存的對象的類型,從而不須要向下轉型以及消除犯錯誤的可能,這樣不是更有意義嗎?這種解決方案被稱爲參數化類型機制。
參數化類型就是一個編譯器能夠自動定製做用於特定類型上的類。
10.對象的建立和生命期
怎樣才能知道什麼時候銷燬這些對象呢?當處理完某個對象以後,系統某個其餘部分可能還在處理它。
1)對象的數據位於何處?怎樣控制對象的生命週期?C++爲了追求最大的執行速度,對象的存儲空間和生命週期能夠在編寫程序時肯定,這能夠經過將對象置於堆棧(它們有時被稱爲自動變量(automatic variable)或限域變量(scoped variable))或靜態存儲區來實現。這種方式將存儲空間分配和釋放置於優先考慮的位置,但也犧牲了靈活性。
2)第二種方式是在被稱爲堆(heap)的內存池中動態地建立對象。在這種方式中,直到運行時才知道須要多少對象,它們的生命週期如何,以及它們的具體類型是什麼。這些問題的答案只能在程序運行時相關代碼被執行到的那一刻才能肯定。若是須要一個新對象,能夠在須要的時刻直接在堆中建立。
由於存儲空間是在運行時被動態管理的,因此須要大量的時間在堆中分配存儲空間,這可能遠遠大於堆棧中建立存儲空間的時間。在堆棧中建立存儲空間和釋放存儲空間一般各須要一條彙編指令便可,分別對應將棧頂指針向下移動和向上移動。建立堆存儲空間的時間依賴於存儲機制的設計。
動態方式有這樣一個通常性的邏輯假設:對象趨向於變得複雜,因此查找和釋放存儲空間的開銷不會對對象的建立形成重大沖擊。動態方式所帶來的更大的靈活性正是解決通常化編程問題的要點所在。(這一段還沒有明白)
Java徹底採用了動態內存分配方式。每當想要建立新對象時,就要用new關鍵字來構建此對象的動態實例。
若是是在堆上建立對象,編譯器就會對它的生命週期一無所知。Java提供了被稱爲「垃圾回收器」的機制,它能夠自動發現對象什麼時候再也不被使用,並繼而銷燬它。
垃圾回收器提供了更高層的保障,能夠避免暗藏的內存泄露問題。
11.異常處理:處理錯誤
異常是一種對象,它從出錯地點被「拋出」,並被專門設計用來處理特定類型錯誤的相應的異常處理器「捕獲」。
異常處理就像是與程序正常執行路徑並行的,在錯誤發生時執行的另外一條路徑。
12.併發編程
對於大量的其餘問題,咱們只是向把問題切分紅多個可獨立運行的部分(任務),從而提供程序的相應能力。在程序中,這些彼此獨立運行的部分稱之爲線程,上述概念被稱爲「併發」。
併發看起來至關簡單,但有一個隱患:共享資源。所以,整個過程是:某個任務鎖定某項資源,完成其任務,而後就釋放資源鎖,使其餘任務可使用這項資源。
13.Java與Internet(暫時跳過)