Java編程思想:第一章:對象導論

第一章:對象導論

    我們之所以將自然界分解,組織成各種概念,並按其含義分類,主要是因爲我們是整個口語交流社會共同遵守的協議的參與者,這個協定以語言的形式固定下來...除非贊成這個協定中規定的有關語言信息的組織和分類,否則我們根本無法交流。


抽象過程

    所爲的編程語言都提供抽象機制。可以認爲:人們所能夠解決的問題的複雜性直接取決於抽象的類型和質量。所謂的“類型”指“所抽象的是什麼?”,比如彙編語言是對底層機器的輕微的抽象。接着出現的命令式語言就是彙編語言的抽象。

    面向對象方式通過向程序員提供標示問題空間中的元素的工具而進了一步。這種標示方式非常通用,使得程序員不會受限於任何特定類型的問題。我們將問題空間中的元素及其在解空間中的標示稱爲對象。這種思想的實質是:程序可以通過添加新類型的對象使自身適應於某種特定的問題,因此,當你在閱讀描述解決方案的代碼的同時,也是在閱讀問題的描述。所以,OOP允許根據問題來描述問題,而不是根據運行解決方案計算機來描述問題。但是仍然與計算機有關聯:每個對象都看起來有點像一臺微型計算機:具有狀態,還有操作,用戶可以要求對象執行這些操作。

    面向對象的5個基本特性:

    • 萬物皆爲對象,將對象視爲奇特的變量,它可以存儲數據,還可以要求它自身上執行一些操作。理論上講,你可以抽取待求解問題的任何概念化結構(狗)。將其表示爲程序中的對象。

    • 程序是對象的集合,它們通過發送消息來告知彼此所要做的。

    • 每個對象都有自己的由其他對象所構成的存儲。可以通過創建包含現有對象的包的方式來創建新類型的對象。

    • 每個對象都擁有其類型。每個對象都是某個類的實例,類就是類型的同義詞。

    • 某一個特定類型的所有對象都可以接收相同的消息。比如子類能處理的消息,父類也能處理,面向接口編程的概念。

    對象具有狀態,行爲和標識。意味着每一個對象都可以擁有內部數據,方法,並且每一個對象都可以唯一的與其他對象區分開來,具體來說,每一個對象在內存中有唯一的的地址。

每一個對象都有一個接口

    在程序執行期間具有不同的狀態而其他方面都相似的對象會被分組到對象的類中,這就是關鍵字Class的由來。創建抽象數據類型(類)是面向對象程序設計的基本概念之一。抽象數據類型的運行方式與內置(built-in)類型幾乎完全一致。你可以創建某一類型的變量,然後操作這些變量。每個類的成員或元素都具備某種共性。同時,每一個成員都有自己的狀態,每一個對象都屬於定義了特性和行爲的某個特定的類。

    面向對象用class來表示數據類型。因爲類描述了具有相同特性(數據元素)和行爲(功能)的對象集合,所以一個類實際上就是一個數據類型。

    一擔類被建立,就可以隨心所欲的創建類的任意個對象,然後去操作它們。事實上,面嚮對象語言中的挑戰之一,就是在問題空間元素和解空間的對象之間一對一的映射。每個對象只能滿足某些請求,這些請求由對象的接口(interface)所定義,決定接口的便是類型。接口確定了對某一特定對象所能發出的請求。但是,在程序中必須滿足這些請求的代碼,這些代碼月隱藏的數據在一起構成了實現。通常爲概括爲,向某個對象發送請求,此對象便知道次請求的目的,然後執行對應的程序代碼。


每個對象都提供服務

    對象就是服務提供者。提供的服務儘量單一職責哦。


被隱藏的具體實現

    將程序開發人員按角色分:

    • 類創建者

    • 客戶端程序員

    類中某些部分需要隱藏,這樣的隱藏部分是程序中脆弱的部分,這樣類創建者可以隨意的修改這些隱藏的部分。也避免客戶端程序員直接調用這類隱藏部分,這對客戶端的bug也大大的減少。

    在任何的交互環境中,就有關係所涉及的各方多遵守的邊界是十分重要的。當創建一個類庫時,就建立了與客戶端程序之間的關係,他們同樣也是程序員,但是他們是使用你的類庫來構建應用,或者構建更大的類庫的程序員。如果所有的類成員都任何人都可用的,那麼客戶端程序員對類庫做任何事情,而不受任何約束。

    因此,訪問控制存在原因是:

    • 讓客戶端程序員無法接觸及他們不應該接觸的部分:這些部分對數據類型的內部操作來說必須的,但並不是用戶解決特定問題所需要的接口的一部分。這對客戶端程序員來說是一種服務,因爲他們很容易的看出那些東西對他們來說很重要,而那些東西可以忽略。

    • 允許庫設計者可以改變類內部的工作方式而不用擔心影響到客戶端程序員。


複用具體實現

    產生一個可複用的對象需要豐富的經驗和觀察力的,但是你一旦有了這樣的設計,它就可以複用。代碼複用時面向對象提供的最了不起的優點之一。

    最簡單的複用某個類的方式就是直接使用該類的一個對象,因此也而已將那個類的一個對象置於某個新的類中。比如:創建一個成員對象。使用現有的類合成新的類稱爲:組合(composition)。如果組合是動態發生的,那麼它通常稱爲聚合(aggregation)。組合通常被視爲擁有(has-is)關係,比如:汽車擁有引擎。

    組合帶來極大的靈活性。新類的成員對象通常都被聲明爲private,使得使用新類的客戶端程序員不能訪問他們。開發時優先考慮組合,而不是繼承。


繼承

    現有的類爲基礎,複製它,然後通過添加和修改這個副本來創建新類就稱爲:繼承。當源類發生改變時,被修改的副本(子類)也會反映出這些變動。

    類型不僅僅與描述了作用於一個對象集合上的約束條件,同時還有與其他類型之間的關係。兩個類型可以有相同特性和行爲,但是其中一個特性可能比另一個含有更多的特性,而且可以處理更多的消息。繼承使用基類型和子類型的概念表示了這種類型之間的相似性。一個基類型包含其所有子類型所共享的特性和行爲。可以創建基類型來表示系統中某些對象的核心概念,子類型來表示實現的各種不同方式。

    當繼承現有的類型時,也就是創造了新的類型。這個新的類型不僅包含現有類型的所有成員,而且更重要的就是它複製了基類的接口。也就是說,所以可以發給基類對象的消息同時也可以發送給子類對象。由於通過發送給類的消息的類型可知類的類型,意味着子類與基類具有相同的類型。改變基類的方法的行爲稱:覆蓋。


是“一個”和“像一個”的關係

    子類對象來替代基類對象,稱爲:純碎替代。通常稱爲替代原則。在某種意義上,這是處理繼承的理想方法。我們經常將這種情況下的基類與子類的關係稱爲:is-a是一個關係。判斷是否繼承,就是要確定是否可以使用is-a來描述類之間的關係。

    有些子類需要擴展基類,這個新的類型也可以替代基類,但是這種替代並不完美,因爲基類無法訪問子類的新的方法。這種稱爲像一個(is-liek-a)關係。子類有新的方法,所以說子類和基類不是完全相同。

    當你看到替代原則時,很容易會認爲純碎替代是唯一可行的方式,而且事實上,用這種方式設計是很好的。但是你會時常發現,同樣顯然的是你必須在子類的接口中添加新方法,兩種方法的使用場景應該是相當明顯的。


伴隨多態的可互換對象

    在處理類型的層次結構時,經常想把一個對象不當做它所屬的特定類型來對待,而是將其當做其基類的對象來對待。使得可以寫出不依賴於特定類型的代碼。這樣的代碼是不會受添加新類型影響的,而且添加新類型是擴展一個面向對象程序一遍處理新情況的最常用方式。

    但是,子類對象看待基類對象時,仍然存在一些問題。編譯器在編譯時不可能知道執行那一個代碼的。這就是關鍵所在,當發送這樣的消息時,程序員並不想知道那一段代碼被執行,如果不需要知道那一段代碼被執行,那麼添加新類型時,不需要改變調用它的方法,它就能運行不同的代碼。

    編譯器不可能產生傳統意義上的函數調用,在OOP中,程序運行時才能夠確定代碼的地址。所以,當消息發送到一個泛化的對象時,必須採用其他的機制。這既是後期綁定。Java使用一小段特殊的代碼來替代絕對地址調用。這段代碼使用在對象中存儲信息來計算方法體的地址。這樣,根據這一段代碼的內容,每一個對象都可以具有不同的行爲表現。當向一個對象發送消息時,該對象就能夠知道這條消息應該做些什麼。

    將子類看做是它的基類的過程稱爲:向上轉型。


單根繼承結構

    Java中所有的類最終都繼承自單一的基類Object,如下好處:

    • 所有的對象都具有一個公共的接口,所以它們歸根到底都是相同的基類類型。

    • 單根繼承結構保證所有對象都具有某些功能。

    • 單根繼承讓垃圾回收期的實現容易的多了。因爲所有的對象都保證具有其類型信息,因此不會因無法確定對象的類型而陷入僵局。


容器

    通常來說,如果不知道解決某個問題需要多少個對象時,或者它們將存活多久,那麼就不可能知道如何存儲這些對象。如何才能知道多少個空間來創建這些對象?答案是你不可能知道,因爲這些信息運行時才能確定的。

    容器,比如List,Map,Set等。

    從設計的角度來看呢,真正需要的只是一個可以被操作,從而解決問題的序列。需要選擇容器,原因如下:

    • 不同的容器提供了不同類型的接口,與外部行爲。

    • 不同的容器,對某些不用類型的操作具有不同的效率。


參數化類型

    以前用Object來表示通用類型,未知類型,但是向上轉換時失去類型信息,需要獲取時強制類型轉換。泛型,就是向下轉換,一般向上轉換是安全的。除非明確知道所要處理的對象的類型,否則向下轉換是不安全的。比如某個list放入一個元素,取出來時必須知道類型,做個強制類型轉換。否則拋出異常。

    向下轉型和運行期檢查需要額外的運行時間,也需要程序員提供更多的心血,那麼創建這樣的容器,它知道自己所保存的對象的類型,從而不需要向下轉型以及消除犯錯誤的可能,這就是泛型機制。


對象的創建和生命期

    在使用對象時,最關鍵的問題之一便是它們的生成與銷燬方式。每個對象需要生存都需要資源,尤其是內存。當我們不在需要一個對象時,它必須被清理掉,便其佔用的資源被釋放和重用。

    怎樣才能知道何時銷燬這些對象?當處理完某個對象後,系統某個其他部分可能還在處理它:

    • C++一樣,編寫程序時確定。給程序員提供了選擇權。

    • 堆在內存池中動態的創建,直到運行時才確定需要多少個對象,它們聲明週期如何,以及它們的具體類型是什麼。

    動態方式有這樣一般性的邏輯假設:對象趨向於變的複雜,所以查找和釋放存儲空間的開銷不會對對象的創建造成重大沖擊。Java採用的就是動態內存分配方式。當需要創建對象時,就要用new關鍵字來構建此對象的動態實例。

    對象的生命週期,垃圾回收期的工作了。Java的垃圾回收器用來解決內存的釋放問題。它知道對象何時不再被使用,並自動釋放對象佔用的內存。結合瞭如下兩種特性:

    • 繼承與單一基類Object。

    • 只能只用方式創建對象(在堆上創建)。


異常處理,處理錯誤

    異常處理將錯誤處理直接置於編程語言中,有時甚至置於操作系統中。Java引入了異常處理,而且強制你必須使用它。它是唯一可接受的錯誤報告方式。如果編寫錯誤的異常處理代碼,就編譯時就有出錯信息。

    異常處理不是面向對象的特徵。


併發編程

    Java的併發內置於語言中。


Java與Internet

  • 客戶端,服務端編程

  • 瀏覽器,服務端編程


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章