之因此寫下這篇文章,是由於女友這學期要修Java,但在這以前只接觸過C語言,對於面向對象的概念一時難以理解,因而這裏寫一篇文章來說一講。我以前並無接觸過Java,本來只是打算講講OOP
的一些概念的,不事後來仍是打算開始學習一下Java,而且整理一個筆記系列,這篇文章就作個開頭吧。有關Java的內容基本上是基於對《On Java 8》和《Java核心技術 卷一》這兩本書。
把這些內容發佈在公衆號上,但願也能對有相同需求的人提供幫助,可是須要注意,在我後續的文章裏,都假定讀者有必定C語言基礎。java
抽象是計算機科學中一個很是重要的概念,它和具體正好相反。抽象是爲了下降複雜度。在文學做品中經常以具體事物來比喻抽象的東西:程序員
一往情深深幾許?深山夕照深秋雨。
可是具體的事物每每是繁瑣的複雜的,在計算機科學中,經過抽象,可以更好的解決問題。
首先咱們在課堂上應該都聽過老師講解二進制時講過二進制的1
和0
與計算機內部的電路開關狀態對應,CPU中有很是很是多的電路。讓咱們來看一下咱們的計算機程序,無論是一個簡單的計算1+1
仍是一個相似Windows這樣的操做系統,它們在運行的時候,其實都是這些電路中電子在跑來跑去。CPU內部有數以億計的晶體管。
在一堆晶體管的基礎上,咱們抽象出門電路,與門、或門、非門等等,不用知道在電路中電子怎麼運動的,也不須要知道輸入多少電壓對應二進制的哪一位,只要知道有什麼樣的輸入能獲得什麼樣的輸出就好了。
在這之上,又能夠抽象出更復雜的電路,觸發器、寄存器,咱們只要知道怎麼操做會獲得什麼結果,而沒必要知道怎麼獲得的結果。
最初的程序,咱們使用1和0
組成的機器語言編寫,它能直接被計算機CPU識別運行。可是對於人類來講仍是難以理解。
在機器語言的基礎上,產生了彙編語言。彙編語言是對機器語言的一種抽象,以下是一個實現兩數相加的彙編程序:編程
datas segment x dw 1234h y dw 5678h z dw ? datas ends codes segment assume cs:codes,ds:datas start: mov ax,datas mov ds,ax mov ax,x add ax,y mov z,ax mov ah,4ch int 21h codes ends end start
在彙編中,使用例如mov
、add
這樣的指令,咱們不用操心具體寫什麼樣的機器碼,彙編器會最終幫咱們將代碼翻譯成機器語言。但在彙編中,咱們還要操心例如將數據移動到某個CPU的寄存器中這樣的問題。
再對彙編進行抽象,咱們發展出了高級語言,例如在Python中:架構
print(2+3)
這樣不只能計算加法的結果還能顯示在屏幕上,咱們編寫的代碼更解決人類的天然語言,不用去關心數據是否是要從內存放進CPU,怎麼實現了數據的相加,怎麼將數據顯示在屏幕中。
好了,如今咱們有了一個高級語言,如今咱們要寫一些程序大體是這樣:函數
函數 函數1 函數2 函數3 函數4 ... 數據 數據1 數據2 數據3 數據4 ...
這時候是一種面向過程的編程模式,隨着時代的發展,當咱們的需求愈來愈多,一個程序可能會寫出很是很是多的函數,有一些函數要實現的功能相似,但又不得很少寫不少行代碼。
以一個工廠舉例,一家工廠,須要工人,生產線,原材料採購,市場銷售,技術研發,若是用面向過程的思想去解決,那麼這個程序員要考慮一家工廠從設計產品到購買材料到投入生產到銷售這一系列環節中全部的功能,全部的數據。
這個時候就須要面向對象的思想了。工具
面向對象編程(Object-Oriented Programming OOP)是一種編程思惟方式和編碼架構,面向對象是一種對現實世界理解和抽象的方法。學習
在工廠的例子中,使用面向對象的思路來解決,那麼咱們會將問題分而治之,讓工人來負責生產,技術員來負責研發,銷售負責販賣產品,採購負責買材料。優化
如下摘錄自《On Java 8》:編碼
- 萬物皆對象。你能夠將對象想象成一種特殊的變量。它存儲數據,但能夠在你對其「發出請求」時執行自己的操做。理論上講,你老是能夠從要解決的問題身上抽象出概念性的組件,而後在程序中將其表示爲一個對象。
- 程序是一組對象,經過消息傳遞來告知彼此該作什麼。要請求調用一個對象的方法,你須要向該對象發送消息。
- 每一個對象都有本身的存儲空間,可容納其餘對象。或者說,經過封裝現有對象,可製做出新型對象。因此,儘管對象的概念很是簡單,但在程序中卻可達到任意高的複雜程度。
- 每一個對象都有一種類型。根據語法,每一個對象都是某個「類」的一個「實例」。其中,「類」(Class)是「類型」(Type)的同義詞。一個類最重要的特徵就是「能將什麼消息發給它?」。
- 同一類全部對象都能接收相同的消息。這實際是別有含義的一種說法,你們不久便能理解。因爲類型爲「圓」(Circle)的一個對象也屬於類型爲「形狀」(Shape)的一個對象,因此一個圓徹底能接收發送給"形狀」的消息。這意味着可以讓程序代碼統一指揮「形狀」,令其自動控制全部符合「形狀」描述的對象,其中天然包括「圓」。這一特性稱爲對象的「可替換性」,是OOP最重要的概念之一。
Grady Booch 提供了對對象更簡潔的描述:一個對象具備本身的狀態,行爲和標識。這意味着對象有本身的內部數據(提供狀態)、方法 (產生行爲),並彼此區分(每一個對象在內存中都有惟一的地址)。spa
在Java中使用class
關鍵字定義類,使用例如Worker w1 = new Worker()
來實例化類爲一個對象。
/* 這是一個類,它表示一種類型,例如工人,就是一類人, 它有name這個屬性(field),work這個方法(method) */ public class Worker { String name; public void word() { ... } } /* w1是一個對象,對象是類的實例,好比工廠裏有100個工人,w1就是工人中的一個實例 w1這個對象的name屬性是張三 w1能夠調用work方法 定義了類以後,每個這個類的實例對象,都會有類定義的屬性與方法 */ Worker w1 = new Worker(); w1.name = "張三"; w1.work();
面向對象編程將數據以及對數據的操做打包起來放到對象裏,外界無需知道程序內部實現的細節,只要經過相似xx.xx()
的形式去調用,封裝能夠隱藏起對象的屬性和實現細節。
咱們能夠把編程的側重領域劃分爲研發和應用。應用程序員調用研發程序員構建的基礎工具類來作快速開發。研發程序員開發一個工具類,該工具類僅嚮應用程序員公開必要的內容,並隱藏內部實現的細節。這樣能夠有效地避免該工具類被錯誤的使用和更改,從而減小程序出錯的可能。彼此職責劃分清晰,相互協做。當應用程序員調用研發程序員開發的工具類時,雙方創建了關係。應用程序員經過使用現成的工具類組裝應用程序或者構建更大的工具庫。若是工具類的建立者將類的內部全部信息都公開給調用者,那麼有些使用規則就不容易被遵照。由於前者沒法保證後者是否會按照正確的規則來使用,甚至是改變該工具類。只有設定訪問控制,才能從根本上阻止這種狀況的發生。
Java 有三個顯式關鍵字來設置類中的訪問權限:public(公開),private(私有)和protected(受保護)。這些訪問修飾符決定了誰能使用它們修飾的方法、變量或類。
public
(公開)表示任何人均可以訪問和使用該元素;private
(私有)除了類自己和類內部的方法,外界沒法直接訪問該元素。private 是類和調用者之間的屏障。任何試圖訪問私有成員的行爲都會報編譯時錯誤;protected
(受保護)相似於 private,區別是子類(下一節就會引入繼承的概念)能夠訪問 protected 的成員,但不能訪問 private 成員;default
(默認)若是你不使用前面的三者,默認就是 default 訪問權限。default 被稱爲包訪問,由於該權限下的資源能夠被同一包(庫組件)中其餘類的成員訪問。使用訪問控制,咱們讓使用咱們編寫的類的程序員不要接觸咱們不想讓他們訪問的部分,同時咱們能夠修改程序,優化咱們的代碼而不影響應用程序員的使用。
public class HelloWorld { public static void main(String[] args) { System.out.println("Hello World"); } }
在上面這個簡單的helloworld程序中,咱們使用了System.out.println()
來打印文字到屏幕上,可是咱們並不知道這個方法的實現細節,哪怕在版本更新中重寫了這個方法的實現,只要接口和功能沒變,咱們的代碼就沒有問題。
隨着工廠不斷髮展,業務越作越多,出現了各類不一樣的職位分工,這些員工在不少方面有類似之處,但具體作的事情略有不一樣,那咱們是否是要寫不少個員工類呢?豈不是會有不少重複的代碼嗎?
咱們能夠這樣作:先定義一個員工類,它擁有名字、年齡、薪水、工做時間等等公有的屬性與方法,而後有各類類型的員工繼承這個工人類,員工類能夠稱爲父類,細分的工種稱子類。
子類會擁有父類全部的屬性與方法。父類作出改變,也會反映在子類上。
子類也能夠添加本身獨有的屬性與方法,例如工人可能會有領取防禦用品的方法。
在咱們的例子中,咱們經過員工類派生出各類類型的子類,有工人、銷售、研發等等,他們都繼承了父類的work()
方法,但是,不一樣的職位作的工做會相同嗎?
答案顯然是否認的。
那麼咱們工廠調用員工去工做,怎麼實現不一樣類型的員工作不一樣的事情呢?要寫上不少的類型判斷嗎?根據不一樣類型完成不一樣行爲?
看下面一個例子:
void toWork(Employee employee) { employee.work(); // ... }
使用toWork()
方法,表面看上去只能使用Employee
類,讓咱們接着往下看:
Work work = new Work(); Technician tech = new Technician(); Salesman sale = new Salesman(); toWork(work); toWork(tech); toWork(sale);
能夠看到傳入任何類型的員工都能獲得執行。
能夠看到咱們沒有作任何的類型判斷,因爲工人、技術員、銷售都是員工的子類,在toWork()
中它們都能被看成員工
類型,這種特性在Java中被稱爲向上轉型(upcasting)
。
發送消息給對象時,若是程序不知道接收的具體類型是什麼,但最終執行是正確的,這就是對象的「多態性」(Polymorphism)。面向對象的程序設計語言是經過「動態綁定」的方式來實現對象的多態性的。編譯器和運行時系統會負責對全部細節的控制;咱們只需知道要作什麼,以及如何利用多態性來更好地設計程序。
面向對象歸根結底是一種程序設計的思想,並非有class
關鍵字就是面向對象。經過這種設計思想,咱們在應對複雜龐大的問題時,能寫出可讀性更高、複用性更強的程序。若是你去閱讀Linux內核的源代碼,會發現雖然其使用C語言
編寫,但仍然有着面向對象的思想在其中。
掃碼關注公衆號: