Java是一門面向對象的語言,它的一個優點在于只針對待解問題抽象,而不用為具體的計算機結構而煩心,這使得Java有完美的移植性,也即Java的口號"Write Once, Run Anywhere"。
所謂的抽象過程,可以理解為對待解問題建模。比如待解問題是一個人,那么我們可以對人進行建模,它的類型是人,有屬性姓名、性別、年齡,還有行為吃飯、走路。Java能直接完全據此建模編碼,而無需考慮具體的計算機結構。所以當我們閱讀Java程序時,正如書上說的"當你在閱讀描述解決方案的代碼的同時,也是在閱讀問題的表述"。
1.2對象的屬性正如我們所觀察到的,人有屬性姓名和年齡,圓有屬性半徑,三角形有屬性邊長和角度,每個對象都有一定的屬性,也可以稱屬性是對象的成員。
1.3每個對象都提供服務每個對象都提供服務也是面向對象的一個重要的思想。
就像在人類世界里,造房子是為了住宿,造車子是為了出行,造學校是為了教育,造醫院是為了醫治。同樣的,在Java中造對象當然是為了給用戶使用。如果所造的對象沒有一點用處,不能提供任何有用的服務,那簡直成了垃圾對象。
既然每個對象都能提供服務,就意味著對象能做一些事,有一些公共方法可以被其他對象使用,這些公共方法也稱為接口。
1.4每個對象都有一個接口面向對象有一個最重要的思想——物以類聚,各從其類。大千世界,無論何物,它都被視為一個實例化的對象,并且任何一個實例化的對象必定是按照一個1.5被隱藏的具體實現 為什么要把具體實現隱藏起來?除了 1.4 節所述原因,還有另一個重要的原因。要知道這編程世界有兩種程序員,一者曰類創建者(那些創建新數據類型的程序員),二者曰客戶端程序員(那些在其應用中使用數據類型的類消費者)。最普遍的,假設稱Java類庫的創建者為A1(即類創建者),稱我們這些使用Java類庫的程序員為A2(即客戶端程序員)。A1與A2形成一種關系,A1負責構建類,而A2使用A1提供的類來做開發。假設類創建者開發了一個計算組合數的類Combination,數學上組合數的公式為C(n,m)=m!/n!(m-n)!,我們可以定義兩個方法,一個是計算階乘,另一個是計算組合數,我們不希望計算階乘的方法成為接口暴露出去,因為一旦暴露了,客戶端程序員就可以隨意設置參數,修改內部實現,因此應當把這類具體實現隱藏起來,這樣才能避免被毀壞,減少程序的bug,此外,這樣做對客戶端程序員而言也是一種服務,因為這樣他們就知道哪些方法是可用的,哪些方法是不可用的,還有一個原因是,當我需要修改隱藏的實現時,不會影響到客戶端程序員,比如我原來是使用循環來實現階乘的計算,但后來我發現用遞歸更加好,我這么一改,甚至連方法名都改了,但是這個類的接口依然沒變,所以沒影響到客戶端程序員。 其實不僅要隱藏具體實現,還應當隱藏對象的屬性。這些屬性和具體實現的隱藏在面向對象中被稱為封裝。封裝的意義在于保護了,比如人,他有屬性年齡,我們都知道年齡必須是非負數的,如果沒有封裝起來,一些調皮的程序員就可以隨意篡改了,比如改成 -1 歲,年齡哪能是負數?所以為了防止篡改,保護對象的屬性,就引入了封裝。 復用具體實現是Java的一大特色了。這里涉及了類之間的關系:聚合、組合、繼承、關聯、依賴。 簡單來講,聚合就是擁有關系,但是兩者生命周期不一致,比如人擁有一臺電腦就是聚合關系,其中電腦的壽命到了,但人的壽命還沒到,人的壽命到了,但電腦的壽命還沒到; 組合也是擁有關系,更是一種強聚合關系,兩者的生命周期是一致的。比如人和自己的大腦,兩者誰也不能離開誰; 繼承不用多說了,比如蘋果繼承自水果,蘋果是水果;關聯也是擁有關系,不同的是,關聯是一對多的關系,比如一個訂單只能有一個客戶,而一個客戶擁有多個訂單; 依賴就是使用關系,比如一個人要過河,他就需要使用一下船只,在代碼里體現在一個類的方法定義了被依賴類的局部變量,或者被依賴類作為此類方法的參數。 繼承是復用代碼的一種方式,它是指在現有的一個類的基礎上創建一個新的類,被繼承的類稱為基類(或父類、超類),繼承出的類成為導出類(或子類、次類)。 繼承有三種情形,1.導出類什么都沒做,因此導出類與基類完全一樣;2.導出類覆蓋(修改)了基類的方法,即導出類純替代了基類;3.導出類添加了新的方法,還可能覆蓋了基類的方法,此時導出類擴展了基類,實際上我們在繼承時往往都是擴展基類。 在繼承上Java與C++最大的不同就是單根繼承了,每個類都只能繼承自一個類,并且,Java中所有的類都直接或間接地繼承自根類Object,如下圖所示 如 1.7 節所示,我們可以說直角三角形是一個三角形,等邊三角形是一個三角形,但三角形不一定是直角三角形或等邊三角形。假設我們已經設計了三角形的繼承體系,這時我們再設計一個操作三角形的類,類中有一個方法用來填充三角形,但已知有三種三角形,即直角三角形、等腰三角形以及等邊三角形,難道要分別為這三種三角形重載三種填充方法嗎?不必,多態使得只要在參數上接受三角形這種泛化的類型即可,也就是說,無論是哪種三角形,在多態上都視為三角形,因此能被方法的參數所接受,故也都能被填充。比如下面的程序: 可見,因多態機制,代碼更加簡潔。 到此,補充一句面試愛問的問題:封裝、繼承和多態是面向對象的三個特征。 如 1.7 節所示,Java中所有的類最終都繼承自單一的基類,就是Object。這樣的單根繼承結構有三個優點: 我們都知道在聲明數組時必須要指定其大小,而一指定大小就再也不能改了,那問題來了,如果數組的大小仍然不夠那怎么辦?也只能重新再定義一個數組了,這樣子就很不方便了。為解決這個問題Java提供了容器,容器能根據自己所需的元素個數動態地調整容器的大小。容器有Set,Map,List以及棧、隊列、樹等,其中面試最愛問的就是ArrayList了。 在Java中,創建對象只要使用new關鍵字即可,然后對象就被創建在堆中,如果對象是創建在堆棧上,編譯器就能確定它的生命周期,還可以銷毀它,但對象是存在于堆中,編譯器就無法確定對象的存活時間了。為了銷毀對象,釋放內存,Java提供了垃圾回收機制,程序員就不再需要手動銷毀對象了,這對程序的編寫的確是方便了許多。 當然Java這樣做也是有弊端的,因為對象是存在于堆的,因此對對象的創建和操作會慢一些,此外把銷毀對象的工作交給垃圾回收器,這樣子有點不放心,如果程序退出了,但垃圾回收器還沒有銷毀對象呢,因此Java程序占用內存高就聞名世界了。 舉個例子,我們設計一個簡單除法的方法divide 這時如果對 y 賦值為0,程序就運行異常,然后二話不說直接退出,很明顯程序體驗太差,其實這情況還不算太差,如果是為銀行、軍事、航空設計程序,但沒有處理好異常,那樣子該有多危險。所以為了確保程序的健壯性,必須要能處理好異常。 許多程序設計問題都要求,程序能夠停下正在做的工作,轉而解決其他問題。有的程序還要求能夠同時做多件事情。比如下載,我們希望同時下載多個資源,支持斷點續傳,可以暫停下載;比如QQ在線狀態,可以一邊聊天一邊查看好友的狀態。對這些問題,只能使用多線程來解決。
// 不使用多態void fill(直角 e) {
1.10容器int divide(int x, int y) { return x / y; }
新聞熱點
疑難解答