java基本思想

面向對象java

衆所周知,面向對象編程思想是java的基本思想。java的整個體系和技術實現都是以這個思想爲基礎。(這個經過類和接口能看出來,後面提到)程序員

對這個事情的認知度甚至變成了不少公司的面試標準。好比有的公司會問你什麼是面向對象、面向對象和麪向過程的區別、面向對象有哪些特性。面試

不過細心的人會發現,沒有公司會問你,你是怎麼將面向對象思想應用到實際工做中的。這是由於:
其一,大多數人瞭解面向對象的思想是爲了應付面試;
其二,確實不多有人會在實際工做中使用面向對象的思想。
第一點是由於教育機構(大學、培訓機構)的老師大都是從c也就是面向過程的語言走過來的。他們的思想已經習慣了使用面向過程編程。而後老師們就使用這個思想去教導學生,這就造成了新一代年輕的程序員也會習慣面向過程的思想去思考問題。師徒制也存在這個問題。如此往復循環,就造成了第二點。而如此積累,當工做量逐漸加大的時候就會出現程序架構設計不合理、數據庫設計不方便、不想寫註釋、不知道些什麼註釋等狀況。而後就變成了「臥槽這代碼是我寫的麼???」。這個時候再趕上個需求變動,心態估計就崩了。數據庫

在這裏感慨一下,真正將面向對象的思想落地,纔是高級編程語言程序員的出路。(有興趣的能夠了解一下領域模型驅動,就是常說的DDD)編程

對於面向對象思想的我的理解:架構

面向對象思想,是一種將現實世界抽象成代碼的思想。
即經過將現實世界中獨立個體(事物)的屬性和行爲封裝在一個類中,經過抽象和繼承實現個體間的相互影響、協做。造成了對象多態和行爲多態的特性。
話說得稍微有點抽象了。簡單來講就是把實際轉換成代碼,代碼之間相互做用造成了代碼間的相互協調,表現了實際中一個事物的多種形態。這種思想叫作面向對象的思想。數據庫設計

與面向過程的區別編程語言

面向過程就是分析出解決問題所須要的步驟,而後用函數把這些步驟一步一步實現,使用的時候一個一個依次調用就能夠了;面向對象是把構成問題事物分解成各個對象,創建對象的目的不是爲了完成一個步驟,而是爲了描敘某個事物在整個解決問題的步驟中的行爲。
因此,面向對象相較於面向過程來講更具備擁抱變化的能力。
說到這裏,忍不住跟看這篇文章的人推薦一部電影:《模仿遊戲》。主演是鼎鼎大名的卷福。故事情節大體是主角爲了破譯德軍的密碼建造了一個機器。聚焦於機器的建造過程來看,機器使用了窮舉法來實驗密碼的正確性,設計這個機器就是爲了破解這個密碼,這樣來看是一個面向過程的設計思想。聚焦於整部電影來看,主角建造這個機器有一部分緣由是爲了記念兒時的玩伴,甚至還給機器取了玩伴的名字。當須要破譯密碼的時候就把工做交給這個機器,機器計算完了以後會返回結果。主角在心裏裏把這個機器當作了一我的,在思想上將這我的抽象成了機器的實現邏輯。這樣來看就是面向對象的思想了。這個機器也是人工智能的起源——圖靈機。函數

咳咳,有點扯遠了。不過我確實推薦朋友們找一找本身專業相關的電影看看,能極大程度提升對專業的興趣和積極性。this

分割線1,其實這裏也沒有特別長,可是下面主要討論技術和思想了,給上面的一些閒扯作個分隔。
封裝

正如上面所說,java體現面向對象思想的一個基本點就是封裝。這個動做的設計初衷就是將現實世界中的事物描述成代碼。因此設計了一個容器叫作類,類中能夠放描述事物的屬性(成員變量)和描述事物的行爲(成員方法)。
封裝完類以後將一些類分門別類的放在對應的包中,而後整合成了一個項目。如此一來,經過封裝這個動做就把一系列功能封裝成了對象。

接口(抽象)

單純從封裝這個動做來看,將實際事物封裝爲類的思想過程就是抽象。而經過抽象,能夠把一些類中的共同點聚集到一個類中複用。那麼將不一樣實現相同名字的變量和方法抽象到一個不用寫邏輯實現的類中,這個類就叫作接口。這個接口就是最高層次的抽象了。

那麼從實際工做來看,能夠從兩個格局理解接口。
一個是系統級的戰略格局,一個是代碼級的戰術格局。

從戰略格局來看,系統之間的相互交互須要制定規則和標準;制定了統一的規則和標準以後,你們按照這個標準去開發。這樣能夠作到多個協調系統之間的並行開發,以及系統之間的耦合性下降。
戰略上,接口定義了系統之間相互協調通信的方式和入口。(從單個系統看其餘的系統,接口也描述了其餘系統的功能)

從戰術格局來看,類能夠描述實際事物的具體屬性和行爲。也就是說類能夠表明一個具體事物,可是沒有辦法表示事物能夠實現的功能。
舉個例子說明:dog類能夠描述狗。那麼二哈和拉布拉多屬於狗的品種,是狗的一種屬性,所應該做爲dog類中的一個屬性(成員變量)。而狗能夠啃骨頭,能夠定義成dog類中的一個成員方法。
可是拉布拉多能夠作導盲犬,二哈能夠作雪橇犬。固然你能夠把這個當作狗的職業封裝爲一個屬性,可是顯然是不合適的。由於,導盲犬和雪橇犬能夠完成的動做沒有幾個同樣的。動做須要定義成方法。因此這個時候須要一個新的類來定義這兩個職業,而不能封裝爲一個屬性。
但是,經過繼承的方式實現由有了單根性的限制(一個類只能繼承一個類)。對於代碼描述事物的功能就很麻煩了。因而java就使用接口解決了這個問題。
因此從戰術格局來看,接口描述的是對象的功能(類描述的事對象的屬性,這個後面說)。

抽象類(抽象和細節)

抽象類實際上是類到接口中間的一個過分產物。

從代碼上來看,抽象類既能夠定義複用的方法邏輯還能夠定義須要子類實現的抽象方法。
因此說,抽象類是在類設計以後才抽象出來的。
對比接口,能夠說接口是設計出來的結果而抽象類是重構出來的結果。二者方向不同。是不一樣過程的產物。

實現類(細節)

類是描述實際事物的基本單位,而這裏要說的實現類,則是特指實現了接口的類。
這種類雖然在本質上跟普通的類是同樣的,可是意義不一樣。
實現類是用來實現接口的邏輯的,他有接口的規則限定。而普通的類則沒有這麼明確的限定。
因此實現類的命名通常是接口名+Impl組成;而普通類根據實際要表明的事物命名便可。

這裏小總結一下,接口是抽象的規則,實現類是具體的細節。二者缺其一,那麼這個系統從設計上來說是不完整的。接口是編寫代碼以前設計出來的;其目的是制定規則,以即可以進行並行開發和擁抱變化。而抽象類,是抽象和細節的混合物,是在開發階段重構出來的結果,目的是複用代碼。

繼承

繼承是面向對象中思想中的一個概念,其實基類拓展超類實如今一個類中能夠直接調用另外一個類的方法是經過拓展指針實現的(extends)。不過從實現效果上來看,跟領域模型中的父子很是相像。因此就把基類稱爲子類,超類稱爲父類了。

extends

繼承的技術實現就是利用extends關鍵字,其實他就是個指針而已。(這裏不叫引用,由於它確實和java中的引用不太同樣,因此加以區別、便於理解)
先來看一段代碼:

public class A{}
public class B extends A{}

你們都知道B類繼承了A類因此B中可使用A中的構造方法和成員(變量和方法)。
這是由於當編譯器檢測到B類後面的extends關鍵字時,會把其後的類名(也就是A)的地址(類的徹底限定名)編譯在B類的class源碼中。

以後運行的時候,類加載器會在加載B類的時候將A類的信息同時加載到方法區中;
而java默認調用子類構造方法的時候會先調用父類的構造方法。
那麼這個時候經過B類實例化出來的對象天然就能調用A類的方法了。

因此,這樣來看。extends關鍵字其實就是一個指針變量,其值爲class定義時關鍵字後面的類名,也就是拓展類的地址。

extends翻譯過來就是拓展的意思。因此B extends A。B就能調用A中的成員。反過來A並無拓展B ,因此A就不能調用B中的成員。這就造成了子類可使用父類的成員,而父類不能使用子類的。
並且爲了保證程序的設計順序性和可讀性,java限制了兩個類互相拓展。就是說當B extends A時,A就不能再拓展B了。再加上拓展的單根行,效果就變成了一個父類能夠有多個子類,而一個子類只能有一個父類。這樣若是把成員看作財產的話;效果就跟領域模型(現實社會)中的父子很是相像了。因此基類和超類也被稱爲子類和父類,而拓展也被稱做繼承。

implments

接口的設計一是爲了打破java繼承的單根性,二是爲了跟接口表明的含義更加貼切。
上面說到繼承是單根性的。這樣就形成了父類只能是經過代碼抽象出來的複用代碼類,很難從設計階段定義;並且確實有了多繼承的需求。(接口表明了功能,一個對象能夠同時有多個功能)
因此java設計了接口來表明設計模型中的功能和系統間的規則。

那麼接口中的方法都是沒有具體實現的,因此須要一個類來實現這個接口中的規則。那麼當一個類跟一個接口關聯起來的時候,天然也就稱做這個類實現了這個接口。

接口之間是能夠相互繼承的就像大規則包含小規則;接口和類之間是實現的,由於接口定義的規則最終要經過類來定義運行邏輯。

this

this關鍵字跟extends其實也是一個指針變量,可是this的用法跟c中的宏定義很是相像;因此要跟extends關鍵字加以區分。

this() 調用當前對象的構造方法
this.成員(變量或方法) 調用當前對象的成員(變量或方法)

發生上面兩種狀況的調用時,其實能夠直接想象成new一個本類的對象賦值給引用a。而後把this換成a,就好了。所說的this表明當前調用的對象,就是這個意思。

super

因爲類的構造方法執行順序是: 父類構造方法–>本類成員變量初始化–>本類構造方法。

這樣就還須要一個指向父類對象的指針。因此就設計了super關鍵字。用法和this關鍵字同樣,只是super指向了父類。

super() 調用父類對象的構造方法
super.成員(變量或方法) 調用父類對象的成員(變量或方法)

這裏要說一下extends 、this、super的區別:
三者都可以當作是指針變量,可是extends存放的是類的地址;
this和super存放的是對象的地址。
換句話說extends是在編譯期工做的;而this和extends是在程序運行時工做的。
這樣記憶就不容易混淆了。

多態

多態是從面向對象思想和設計的角度來講的。
當咱們設計了一個類時,在另外一個類中調用這個類的對象,可是表現的好像是這個類的父類;這種狀況就是向上造型。
有些時候咱們須要一個類的子類,可是持有的倒是其父類的引用,這個是咱們須要作一個強制類型轉換來獲得咱們須要的子類。這個操做就是所謂的向下造型。
上下造型在面向對象編程時很是實用。當這種方式在一個系統中頻繁使用的時候,表現出來的效果常常是一個對象扮演多種角色,還能夠相互轉換。這種現象被人們稱爲多態。

對象多態(類型轉換、上下造型)

對象多態是多態中的一種主要表現形式。發生在對象之間,也就是說對象多態的基本單位是對象,不可向下細分。

這裏先說一下實現原理,仍是比較巧妙的。

 


咱們來看,main方法第一行第二行不用解釋,就是建立了兩個對象 (ac表示 aClass bc同理)。
而後會發生以前說extends關鍵字時說過的現象:bc能夠調用A類和本類的成員,也就是變量a和變量b。可是ac卻只能調用A類的成員。緣由在extends那裏說過了這裏就不在詳細說明。

第三行就是咱們常說的向上造型。原理大概是這樣的:
編譯器從左到右檢測代碼,首先檢測到A ab。此時編譯器會讀取A類的信息,並標記有一個局部變量ab數據類型是A。

而後檢測到操做符 = 編譯器會去檢查右側的表達式。若是是字面量表達式(好比3+5或者簡單字符串拼接)那麼會直接在編譯期計算,以後檢查結果的數據類型是否合規;若是是複雜表達式(除了字面量表達式以外的表達式)則只檢查數據類型是否合規。
咱們上面的代碼屬於複雜表達式,編譯器只進行數據類型的檢查。
java語法規定new 後面只能是類的構造函數,也就是說這裏必須實例化一個對象出來。這裏編譯器只須要將new關鍵字後邊的字符拿出來,而後把()和;拿掉,就獲得了實例化對象的數據類型了。
這也就是java要求構造方法必須與類同名的緣由。

而後編譯器拿到了右邊表達式結果的數據類型B跟左邊的數據類型做比較。這個時候extends關鍵字就開始起做用了。因爲B extends A,編譯器會在讀取B類信息的時候同時將A類的地址添到信息中,算做B類信息的一部分。這樣,B就能匹配兩種數據類型,一種是本身,一種是擴展類,也就是A。

這樣在比較左右兩邊數據類型的時候就變成了 A = B|A 。表達式成立,編譯經過。

同理mian方法第四句,因爲A並無拓展B。因此數據類型比較不成立就會編譯報錯。

到這裏就是向上造型的大概實現原理了。一樣的原理也能夠推出繼承的幾種限制,好比成員訪問權限和拋出異常類型的限制。這裏不作詳細介紹了。

可能有人會問上面的第四句,比較左右數據類型的時候不該該是B|A = A嘛?這樣應該也成立呀。
這個實際上是編譯器的識別規則,上面提到了構造方法的執行順序,第一步就是執行父類構造方法。也就是說真正運行起來的時候,只有執行構造方法,纔能有父類的對象,才能調用父類的成員。因此這裏的表達式應該是B = A,是不成立的。

說完了向上造型,向下造型就很好理解了。上面的例子中,A類型的變量ab 實際的值確實B類的對象。
而作.運算的時候,編譯器只根據變量的數據類型決定能.出來的東西。因此父類型的引用,雖然是向上造型,可是仍然不能調用子類的成員。若是某一個時刻咱們確實須要使用子類的成員,可使用強制類型轉換。也就是:

B b = (b)ab;
1
這裏編譯器不會報錯,是由於檢測到了強制類型轉換。可是轉換成功的前提是ab的值確實是B類型的。若是不是,在程序運行的時候會拋出類型轉換異常。

到這裏,上下造型就說完了。利用上下造型極大的提升了程序的靈活性,使得java編程跟面向對象思想更加貼切。在上下造型被大範圍應用的時候,同一個對象能夠賦值給不一樣類型的變量,在程序中某一個運行時段表明的意義也就不同,這就造成了對象多態的效果。

行爲多態(重寫和重載)

行爲多態的基本單位是方法,不可向下細分。行爲多態是發生在對象內部的。
表現爲一個對象在表明的意義不一樣時行爲也不一樣。

這裏提一下重寫和重載的區別,好多面試裏也問道。其實只要搞清楚這兩個動做是怎麼回事就很容易回答這個問題了。

先說重載。通常發生在同一個類中,編譯期就會綁定,方法名相同參數列表不一樣。
這個設計是爲了模仿銀行窗口這樣的對象。假設客戶要取錢,可是事先並不知道客戶要那什麼憑證來取錢。因此設立一個窗口,不管什麼憑證,只要是取錢的業務均可以到窗口辦理。
有了重載就能夠很方便的實現了。

再說重寫。發生在父子類中,運行期纔會肯定,方法名相同參數列表也相同。
注意若是在父子類中方法名相同參數列表不一樣的狀況是重載不是重寫。雖說父子類不是同一個類,可是有拓展關係。就是說在編譯期就能看到兩個類中的成員,因此在父子類中也能夠發生重載。
這裏來舉個例子:

 


仍是上面的AB類,此次在B類中定義了一個跟A類中print方法徹底相同的print();

當在編譯時,第一句向上造型上面說過了。ab.print(1);編譯器會去查看ab變量的數據類型,也就是A類,A類中含有print方法,參數爲int。符合語法,編譯經過。同理第三句也是同樣。

當在運行時,程序會取ab的值,這個時候ab就是一個B對象了,那麼去內存中B對象的領域裏取print方法運行,實際上執行的邏輯是B類中定義的print方法的邏輯。因此就打印了balabala。這個現象好像A的方法被從新寫了一遍同樣,因此就把這種現象叫作重寫了。

而後執行第四句,一樣的邏輯,找到ac的數據類型,比較正確編譯經過;
執行的時候取ac的值,執行A對象的print方法,天然就打印1了。

到這裏重寫和重載就說了個大概了。這兩種語法規定的出現,讓java對象實現了一個對象多種行爲姿態的效果。被稱爲行爲多態。

行爲多態和對象多態放在一塊兒組成了java語言的第三大特性,統稱爲多態。而java語言又是面向對象的表明語言,天然也就說面向對象的第三大特性是多態了。

其實這裏是推崇了一下java,事實應該是先有思想的特性纔有語言的實現。上面所說只是爲了調一下胃口,並不表明真實歷史。 不過單憑java的開源,我的以爲就值得推崇一下。
---------------------
做者:AgentRich
來源:CSDN
原文:https://blog.csdn.net/Agent_Rich/article/details/81986910

相關文章
相關標籤/搜索