序#
《On Java》是一本值得一讀的好書。儘管有Java
的基礎,但在閱讀本書時,在基礎部分還是能獲得新的理解。作者作為《Thinking in Java》的作者,擅長在講解用法之餘,還能向你普及這個知識點的由來,為什麼要用它,它的價值體現在哪裡,它到底是什麼。我不希望讀了一遍後又忘了,所以每章都根據心得體會寫下些什麼,後續也可以直接閱讀本文來回顧。你是不是忘記了什麼中二計劃?
第 1 章 什麼是對象#
這一章主要是給讀者總結Java
的核心特點。沒有學會Java
的讀者可以以小見大,心中有一個對Java
大概的了解;有過基礎的讀者也可以進一步加深印象,更能讀到作者對Java
獨到的見解。
圖靈官方對本章的導讀指南為 “了解即可”。就個人來說的話,本章最主要的知識重點為以下幾點:
抽象的歷程#
所有編程語言都是一種抽象。甚至可以說,我們能夠解決的問題的複雜程度直接取決於抽象的類型和質量。
彙編語言是對機器碼的一種抽象;面向過程編程的 C 語言,它是對彙編語言的一種抽象。在面向過程編程時期,程序員要把自身姿態更多的放到計算機層面,要以計算機的角度看問題如何解決。這樣的抽象類型,還是需要頻繁考慮計算機的結構而非問題本身的結構。
面向對象編程的出現,很好的解決了之前面對過程編程的痛點。按本書說法,早期的程序員需要在機器模型(解決方案空間,也就是計算機)和實際需要解決的問題模型(問題實際存在的空間,也就是業務)之間建立關聯。但面向過程編程建立的關聯抽象程度不高,程序員仍然要考慮計算機,需要花費精力維護這種關聯。而面對對象編程,實際上將機器模型的元素與問題模型的元素通過對象這一載體一一映射,使得程序員可以通過面向對象思想的強大抽象能力在機器模型中直接描述問題模型,解決關聯問題。你閱讀的既是機器模型提供的解決方案 —— 代碼,也是對問題模型中實際業務的描述。顯然這種抽象類型更通用,也更高效。
面對對象編程的宗旨#
面對對象編程的宗旨,就是創建或使用對象提供的某種服務來解決問題。程序也為用戶提供服務,它通過使用某些對象提供的服務來做到這一點。
實現隱藏#
設計一個好的類的技巧之一是隱藏細節。不要事無巨細的暴露細節給使用方,因為你不知道對方會如何使用你的類,這將為今後修改類製造隱患。所以我們對外只提供服務,隱藏其他所有不必要的信息。讓使用方更信任我們的服務不會因更新而失效,我們也能放心大膽的修改重構,不必擔心某段代碼是否有被使用的可能。
復用、繼承與多態#
這裡介紹了一些Java
的重要特點。繼承與多態的目的其實就是為了盡可能的復用代碼。例如當我們需要創建一個與已有的類十分相似的時候,就不用再寫一堆代碼,直接繼承就好了。但實際上應該謹慎考慮使用繼承,繼承的原則在於盡量滿足替換原則,即符合 “is-a” 關係,意思是 “A 是 B”。一個較為清晰的判斷是否需要使用繼承的問題是:“我是否需要向上轉型?”。
在實際編寫過程中,常常需要讓方法不依賴具體的類。方法只要保證傳入的是基類就行了,無需關心傳入的子類具體是什麼,省去了編寫冗長的判斷代碼,交給運行時去執行具體子類的方法就好了,這就是多態。
第 2 章 安裝 Java 和本書示例#
值得一提的是,本章並沒有關於環境變量與
classpath
等的講解,安裝步驟也使用Win
平台的Chocolatey與Mac OS
的HomeBrew這類包管理工具解決了,兩者都會自動處理環境變量。當然,我們需要知道設置環境變量是為了方便在控制台編譯和運行Java
程序,只是現在的IDE
與腳本越來越智能,已經能實現自動設置環境變量了。
第 3 章 對象無處不在#
正所謂 “萬物皆對象”。在本章中,作者將論述Java
中最重要的對象,以及參與Java
程序運行的各方面 “勢力”。圖靈導讀的建議是 “了解運行和創建一個簡單的Java
程序所需知識”,因此本章重點總結以下幾點:
對象與引用#
本節中作者講到,儘管Java
中對象無處不在,但實際上我們不是真正的在和對象打交道。我們都知道,編程語言最終是在內存處理數據的,那麼,如何安全的在內存中處理自然就是一個問題。是直接操作內存好呢?還是用某種方法間接操作內存呢?Java
給出了它認為的理想的答案:使用引用。引用就像遙控器操縱電視機一樣控制對象,調用對象行為,改變對象狀態。
數據保存在哪裡?#
這一節介紹了Java
的數據存儲方式:
- 1. 寄存器。從計組中已經認識到寄存器是存取速度最快的數據存儲方式,毕竟它直接保存在中央處理器,也就是 CPU 裡。不過寄存器寸土寸金,所以
Java
不允許手動控制寄存器的分配。 - 2. 棧。在 RAM 中,處理器可以通過棧指針操作數據。可是根據數據結構我們知道,棧這種數據結構,在增刪方面不夠靈活,因此只有某些數據(比如對象引用)保存在棧上,對象本身卻並非如此。
- 3. 堆。在 RAM 中,堆是一個通用的內存池,用於存放所有
Java
對象。編譯器並不關心堆上對象的生命周期,因為其有對應的分配和清理辦法,而且隨著時代的進步,Java
的堆內存分配機制已經變得非常高效了。 - 4. 常量存儲。常量直接保存在程序代碼中,或 ROM 中。例如所有的字符串和字符串常量,都保存在字符串資源池中。
- 5. 非 RAM 存儲。通常意義就是那些不在內存的數據,比如序列化對象,一種可以發送到其他機器的對象;還有持久化對象,一種保存到磁盤上的對象。這些數據存儲類型可以將對象以其他媒介保存,需要時再轉換回對象。最典型的例子就是數據庫存儲。
第 4 章 & 第 5 章 操作符 & 控制流#
這兩章詳細闡述了Java
的操作符和控制流語法。圖靈導讀的建議是 “都需掌握,無難點”。鑒於基本與C
無異,這裡只記錄一下讀書後比較感興趣的點。
對象賦值需謹慎#
前面說過,我們其實並沒有真正操作對象本身,只是操作對象的引用,所以當執行對象賦值時,其實是將引用複製給了另一個引用,這會導致兩個引用指向同一個對象,而被賦值的引用所對應的對象就沒有引用了。這個示例告訴我們對象賦值需要謹慎,可能會有意想不到的結果。
Java == C++ -- --#
這是Java
創始人高司令的觀點,他認為Java
是C++
去除了一些沒必要又很難的內容,是一種更精簡的語言。當然也不是純減法 2333
== 和!=#
不同方法創建的相同內容的封裝類,內存中存放的位置會有不同。而操作符==
和!=
比較的是對象的引用,所以對對象比較時,最好使用equals()
。
boolean-exp ? value0 :value1
三元操作符#
它主要用於從兩個值中選擇一個給變量賦值的場景。比完成同樣場景的if-else
更緊湊。
for-in
#
float[] f = new float[10];
for(int i=0; i<10; i++)
f[i] = rand.nextFloat();
for(float x : f)
System.out.println(x);
可惜for-in
只能運用於遍歷相關。你並不能修改 f 中的元素。
for(;;)
#
編譯器同等對待
while(true)
和for(;;)
,Java
源碼中對這兩種方法的使用也幾乎平分秋色。
Math.random()
的取值範圍#
其實本來是一個使用字符串的switch
例子。告訴我們Math.random()
的嚴格取值範圍為[0,1)
。