Thinking in Java 筆記,thinkingjava
大二就買了這本書,如今再看這本書,看到了一些以前沒看的細節,也有了不同的體會。本文使用第4版,整理每章的筆記心得。老外的書有個特點,他會花費大量的文字去闡述一個概念,這比堆代碼強多了。
第 1 章 對象導論
1.1 抽象
抽象是電腦最重要的概念之一。C 在解決問題時,主要是基於電腦的結構進行抽象,而不是基於所要解決的問題的結構。而 Java 則是針對問題進行建模,根據問題來描述問題,程式可以通過添加一個新類型的對象使自身適用於某個特定問題,程式員不會受限於任何特定類型的問題。
對象具有狀態、行為和標識。純粹的物件導向程式設計的基本特性:
- 萬物皆為對象:對象可以儲存資料(狀態),也能執行某些操作(行為)。理論上,可以抽取待解決問題中的任何概念化構建為對象。
- 程式是對象的集合,它們通過發送訊息告知彼此所要做的:其實就是調用特定對象的方法。
- 每個對象都有自己和其他對象構成的儲存:通過現有對象產生新對象,繼承和組合。
- 每個對象都擁有其類型:每個對象都是某個類的執行個體 。
- 一類對象可以接收同樣的訊息:多態,子類能夠替代父類完成某些調用。
1.2 類和對象
每個對象都是某個類的執行個體,一個類實際上就是一個抽象資料類型,它描述了具有相同特性(成員)和行為(功能)的對象集合。程式員可以通過定義類來適應問題,而不是被迫使用現有的用來表示機器中的儲存單元的資料類型。根據需求添加新類型擴充程式設計語言,系統就像對待內建類型一樣驗證並管理它們。
把對象看做是服務提供者好處:將問題分解為一系列對象的集合;提高對象的內聚性,每個對象都專註本職工作,就是高內聚性,還有個低耦合,解耦一般就用隊列實現;方便理解代碼和重用。
物件導向的特點:封裝、繼承和多態。何為封裝?即隱藏對象的屬性和細節,僅對外公開介面,控制屬性的讀取和修改。存取控制存在的原因:隔離與提供的服務無關的部分;分離並保護介面和實現。Java 中有四種存取權限,分別是:
- public:對任何人都可訪問
- private:只有類建立者和內部方法可訪問
- protected:與 private 相當,區別是子類可訪問
- 預設存取權限:同一包內的類可相互訪問,不同包如同 private 一樣
1.3 代碼複用
兩種方式,組合和繼承,組合靈活性比較高,相比於繼承耦合度低一些。如果要使用某個類提供的服務功能時,一般用組合,當要是使用類提供的介面時使用繼承。
繼承,使用現有的類建立新類型。子類擁有父類的成員(public, protected)並且複製了父類的介面,也就是說,子類與父類具有相同的類型。子類有兩種方式改變自己的行為:添加新方法和覆蓋父類的方法。當添加新方法時,如果所有子類都需要,就可以把它抽取到父類中。
Java 中,Object是所有類的直接或間接父類。單根繼承的好處有:所有對象具有共用介面,利於向後相容;所有對象都具備某些功能,易於建立和參數傳遞;易於記憶體回收。
1.6 多態
既然父類和子類是相同類型,那麼在運行時子類就能替換父類(向上轉型)來完成不同的功能,這就是多態。多態的體現是:方法的重載和覆蓋。編譯器(靜態分發,重載)和運行系統(JVM動態分發,覆蓋)會處理相關細節,保證程式正確的行為。
1.7 容器和泛型
容器,就是Java中的資料結構了,不同的容器提供不同的介面和行為,對於某些操作具有不同的效率。在JDK 5 之前容器儲存的對象是Obejct,儲存物件必須向上轉型,會丟失其身份,當取出時需要向下轉型,可能會出錯,因為不知道之前放進去的是什麼類型的對象,因此,JDK5增加了泛型,明確指出容器可接收的物件類型。
1.8 對象的建立和生命週期、異常和並發
Java 使用動態記憶體分配技術,使用關鍵詞 new 在堆上建立對象。使用記憶體回收行程釋放對象佔用的記憶體。
Java 內建了異常的處理,而且強制使用。異常提供了一種從錯誤進行可靠恢複的途徑。
並發控制好共用資源的訪問即可,Java提供了並發編程庫,有現成可用的並發資料結構。
1.9 Java與Internet
網路編程會涉及到很多知識,TCP/IP,多線程,IO模型等,要寫出高效能的Java程式,還是要下大工夫的,雖然大問題被JVM搞定了。
第 2 章 一切都是對象
2.1 對象
Java 通過引用來操作對象,使用 new 建立對象。那麼對象被安置在哪個地方呢?計算中有5個地方可以儲存資料,分別是:
- 寄存器:位於CPU內部,最快的儲存區,根據需求進行分配,不能直接控制
- 堆棧:位於RAM,使用堆棧指標上下移動分配記憶體,速度僅次於寄存器,Java對象不在此,但引用在這裡。Java系統知道棧裡的元素確切生命週期
- 堆:位於RAM,存放所有的 Java 對象,分配記憶體比較靈活,但代價是慢
- 常量儲存:常量值通常放在程式碼內部,或者放在 ROM 唯讀記憶體中
- 非RAM儲存:如將對象序列化到磁碟上,或者存在資料庫中
2.2 基本類型
基本類型儲存值,共置於堆棧中,高效。Java 中基本類型的大小是固定的,不隨硬體架構的變化而變化。基本類型如下:
- char:16-bit,2位元組,Unicode字元,0 ~ 2^16-1,Character
- byte:8 bits,1位元組,-128 ~ 127,Byte
- short:16 bits,2位元組,-2^15 ~ 2^15-1,Short
- int:32 bits,4位元組,-2^31 ~ 2^31-1,Integer
- long:64 bits,8位元組,-2^63 ~ 2^63-1,Long
- float:32 bits,4位元組,IEEE754,Float
- double:64 bits,4位元組,IEEE754,Double
- boolean:true/false,Boolean
- void:Void
所有數值均有加號或減號,JDK5的自動封裝功能,自動地將基本類型轉為封裝類型。
高精度數字:BigInteger:支援任意精度的整數;BigDecimal:支援任意精度的定點數。
數組也是對象,能儲存基本類型和參考型別,Java會確保數組被初始化。
2.3 範圍 scope
Java 使用花括弧定義範圍,局部變數在花括弧結束時,生命週期就結束了,而對象不是如此,它能一直儲存下去,Java通過記憶體回收行程管理對象的記憶體。一般不會出現記憶體流失,但也不是絕對的。
2.4 類,欄位,方法
使用 class 關鍵字定義一個類,類擁有欄位(成員變數)和方法,對於成員變數,即使沒進行初始化,Java也會保證它有一個預設值,參考型別預設為null,數字都預設為0,布爾預設false,char預設’\u0000’(null)。對於局部變數編譯器會強制進行初始化。
方法,方法名和參數合起來稱為方法簽名,關於參數,在Java中只有值傳遞。Java消除了向前引用的問題,也就是同一個類中,成員變數和方法的先後順序可以隨意。
static關鍵字可用來修飾欄位,方法和類。修飾欄位方法:表示屬於類,不用建立對象就可使用。一般是修飾內部類,此類與一般的類沒有差別。
2.5 注釋
常用標籤和html如下:
- @see:引用其他類,@see classname,@see fully-qualified-classnam#method-name
- @author:作者資訊
- @version:版本資訊
- <pre>code</pre>:代碼
- <ol><li>1<li>2</ol>:列表
第 3 章 操作符
3.1 優先順序&賦值運算子
從左至右先乘除後加減,當不確定時,使用括弧明確標識即可。
賦值運算子(=),對基本類型賦值就是把一個地方的內容複寫到另一個地方,比如int a=b,就是把b的內容複寫給a;對對象賦值只是使這個變數也指向該對象,比如String s = a,s和a指向同一個對象。將對象傳給方法時,也只是傳遞一個引用的值,或者說傳遞一個對象的別名。
3.2 算術、關係、邏輯運算子,直接常量
加減乘除,模數,一元加、減操作符,自增,自減。
== 作用於基本類型,比較值是否相等;作用於對象比較是否是同一個引用,比較對象使用equals,預設的equals比較引用,需要重寫。
與(&&)、或(||)、非(!)產生一個布爾值,具有短路功能,即如果第一個運算式能確定整個運算式的結果,那麼就不會運算後面的運算式。
直接常量,必須明確告訴編譯器常量類型,比如10F,10D,10L,0xFF。對於char、byte、short超過其最大範圍自動轉為int。
指數計數法:float a = 1.39E-43F;表示1.39×e^-43,如果不加F編譯器預設會當做double處理,會提示類型轉換。
3.3 位操作符和移位操作符
位操作符:
- 與(&):同為1,輸出為1
- 或(|):有1,輸出為1
- 異或(^):不全為1,輸出為1
- 非(~):取反
移位操作符,只能用來處理整數,char、byte、short移位時自動轉為int:
- 左移(<<):低位補0,相當於乘以2^n,n為移動位元
- 右移(>>):使用符號位擴充,為正高位補0,為負高位補1,相當於除以2^n,n為移動位元
- 無符號右移(>>>):高位用0擴充
在進行移位時,比如int只有數值右端的低5位才有用,比如 16>>2 和 16>>34 相等,因為2^5=32,相當於對32模數。long類型就是低6位元字有效。
這裡多說兩句,源碼中或進行移位時會經常看到(&0xFF),原因是什麼呢?
一般我們會對位元組byte進行操作,首先 0xFF 表示低8位(1位元組),當對byte移位操作時,會自動轉成int,而Java中 int類型有32位,並且在電腦中數字使用有符號的二進位補碼錶示。所以byte轉為int時會進行符號擴充,高位以符號位填充。如果byte為正數那麼其補碼與原碼相同,此時進不進行位與操作都無所謂,但是為負數時不同,比如byte a = -4;其轉為int在內部的表示就是11111111111111111111111111111100,這進行運算顯然是不正確的,有效位只有低八位,所以與0xFF位與運算,把高24位置0,運算結果就正確了。