天天看點

Java程式設計思想筆記

#### Java程式設計思想筆記... 持續更新

>最近看新項目的時候,很多代碼看得迷迷糊糊,覺得自己基礎有些差,是以開始啃這本Think in Java。本文僅作為筆記記錄。

##### 第一章:對象導論

   1.面向對象程式設計方式中涉及到的基本特性:

     >.萬物皆為對象。 它可以存儲資料,也可以在自身上執行操作。

     >.程式是對象的集合。它們通過發送消息來告知彼此所要做的。可以把消息想象成 對某個特定對象的方法的調用請求。

     >.每個對象都有自己的由其他對象所構成的存儲。 可以通過建立包含現有對象的包的方式來建立新類型的對象。(疑惑)

     >.每個對象都擁有其類型。  每個對象都是一個類的執行個體。

     >.某個特定類型的所有對象都可以接收同樣的消息。 例如:圓形的對象也是幾何形的對象。這種可替代性是OOP中最強有力的概念之一。

     2.類描述了具有相同特性(資料元素)和行為(功能)的對象集合。一個類實際就是一個資料類型。

     3.Java的三個通路限定關鍵字:public、private、protected

     >.public表示緊随其後的元素對任何人都是可用的。

     >.private關鍵字表示除 類型建立者 和 類型的内部方法 之外的任何人都不能通路的元素。

     >.proteced關鍵字與private作用相當,差别僅在于繼承的類可以通路protected成員,但是不能通路private成員。

     >.Java還有一種預設的通路權限,沒有使用限定詞時,發揮作用。這種權限被稱為:包通路權限。這種權限下,類可以通路在同一個包中的其他類的成員,但是在包之外作用和private一樣。

     4.使用現有的類合成新的類,這種概念叫做:組合(composition),如果組合是動态發生的,那麼它通常被稱為聚合。組合經常被視為“has-a”(擁有關系)。

       >.聚合:整體和部分可以分離,它們可以有各自的生命周期。  例如: 雁群和孤雁

       >.組合:整體和部分不可以分離,成員必須依賴整體才有意義。 例如: 燕子和它的翅膀

     5.繼承使用過多會導緻難以使用并過分複雜的設計。建立新類時,首先考慮組合,因為它更加簡單靈活。

     6.由于基類和子類具有相同的基礎接口,是以伴随此接口必定有某些具體實作。也就是說,當對象接收到特定消息時,必須有某些代碼去執行。

     7.兩種方法可以使基類和子類産生差異:

     >.直接在子類中添加新的方法,這些新方法不是基類接口的一部分。這意味着基類不能滿足你的所有需求。

     >.改變基類的方法的行為,稱為重寫(override)

     8.把将子類看做是它的基類的過程稱為向上轉型。   (多态相關)

     9.單繼承使得垃圾回收器的實作變得更加容易, 垃圾回收器是Java相對C++的重要改進。由于所有對象都保證具有其類型資訊,是以不會因為無法确定對象的類型而陷入僵局。這個系統級操作(例如異常處理)顯得尤為重要。

     10.Java完全采用了動态記憶體配置設定方式:每當想要建立新的對象時,就要使用new關鍵字來建構此對象的動态執行個體。

     11.在堆中配置設定存儲空間所需的時間 可能遠遠大于在堆棧中建立存儲空間的時間。(在堆棧中建立存儲空間和釋放存儲空間通常各需要一條彙編指令,分别對應将棧頂指針向下移動和将棧頂指針向上移動)

     12.允許在堆棧上建立對象的語言,編譯器可以确定對象存活的時間,并可以自動銷毀它。但是在堆上建立對象,編譯器會對它的生命周期一無所知。

     13.把問題切分成多個可獨立運作的部分(任務),進而提高程式的響應能力。在程式中,這些彼此獨立運作的部分稱之為線程。同一個cpu上“同時”(看起來是同時,其實不是的,cpu在各個程式之間切換)運作多個程式。

##### 第二章:一切都是對象

    1.将一切都看作對象,但是操縱的辨別符其實是一個“引用”(reference)。引用可以獨立存在。最好建立引用的同時對引用進行初始化(這樣比較安全,否則調用引用的方法時會報空指針異常)。

    2.五個不同的存儲資料的地方:

    >.寄存器:這是最快的存儲區,它位于處理器内部(其他四種在處理器外部)。但是寄存器的數量極其有限,是以寄存器根據需求進行配置設定,不能直接控制。

       總結:速度最快、位于處理器内部、數量有限、我們控制不了。

    >.堆棧:位于通用RAM(随機通路存儲器)中,但通過堆棧指針可以從處理器那獲得直接支援。堆棧指針若向下移動,則配置設定新的記憶體;若向上移動,則釋放那些記憶體。這是一種快速有效的配置設定存儲方法,僅次于寄存器。建立程式時,Java系統必須知道存儲在堆棧内所有項的确切聲明周期,以便上下移動堆棧指針。這一限制限制了程式的靈活性。對象的引用存儲在堆棧中。

       總結:位于RAM中;通過堆棧指針和處理器關聯;快速有效,速度僅次于寄存器;系統必須知道堆棧中所有項的生命周期,導緻不夠靈活

       注意:記憶體中的堆棧和資料結構中的棧不同。這裡的堆棧對應linux記憶體中的user stack區域(這個區域向下生長),是以指針向下移動配置設定新記憶體。檢視的部落格位址:[原文](https://www.cnblogs.com/mingmingcome/p/8256921.html#commentform)

    >.堆:一種通用的記憶體池(也位于RAM中),用于存放所有的Java對象。和堆棧不同的是:編譯器不需要知道存儲的資料在堆裡存活多長時間。是以,在堆裡配置設定存儲有很大的靈活性。這種靈活性的代價是:用堆進行存儲配置設定和清理可能比用堆棧進行存儲配置設定需要更多的時間。

       總結:位于RAM中,存放所有Java對象;比較靈活;進行存儲配置設定和清理可能比用堆棧需要更多時間;

    >.常量存儲:常量值通常直接存儲在程式代碼内部,這樣做是安全的,因為它們永遠不會被改變。有時,在嵌入式系統中,常量本身會和其他部分隔離開,是以這種情況下可以選擇将其存放在ROM(隻讀存儲器)中。

    >.非RAM存儲: 如果資料完全存活于程式之外,那麼它可以不受程式的任何控制,在程式沒有運作時也可以存在。其中兩個基本的例子是流對象和持久化對象。在流對象中,對象轉化成位元組流,通常被發送給另一台機器。在“持久化對象”中,對象被存放在磁盤上,是以,即使程式終止,它們仍可以保持自己的狀态。這種存儲方式的技巧在于:把對象轉化成可以存放在其他媒介上的事物,在需要時,可恢複成正常的,基于RAM的對象。

    總結:資料不受程式的任何控制,可以存活在程式之外; 把對象轉化成可以存放在其他媒介上的事物,需要時可恢複成基于RAM的對象。

   3.基本類型建立時,變量直接存儲“值”,并且放在堆棧中,比較高效。Java要确定每種基本類型所占存儲空間的大小。Java中基本類型的大小不随機器硬體架構的變化而變化。 這種所占存儲空間大小的不變性是Java程式比其他大多數語言編寫的程式更具有可移植性的原因之一。

    4.所有數值類型都有正負号,沒有無符号資料類型。boolean類型所占存儲空間的大小沒有明确指定,僅定義為能夠取字面值 true 和 false;

    byte 1位元組      

    char short   2位元組

    int  float   4位元組

    long double  8位元組

    5.高精度數字:Java提供了兩個用于高精度計算的類:BigInteger 和 BigDecimal 它們沒有對應的基本類型。

    BigInteger支援任意精度的整數,也就是說,在運算中,可以準确地表達任何大小的整數值,而不會丢失任何資訊。

    BigDecimal支援任何精度的定點數,例如,可以進行精确的貨币計算。

    6.Java確定數組會被初始化,而且不能在它的範圍之外被通路。  這種範圍檢查是以每個數組上少量的記憶體開銷及運作時的下表檢查為代價的,但是換來的是安全性和效率的提高(有時可以優化這些操作)。

     建立一個數組對象時,實際建立了一個引用數組,并且每個引用都會自動被初始化為一個特定值,該值擁有自己的關鍵字null,在使用任何引用之前,必須為其指定一個對象。

     int[] arr = new int[10];         每一個預設值為0

     short[]                                          預設值為0

     byte[]                                           預設值為0

     double[]                                       預設值為0.0

     float[]                                           預設值為0.0

     char[]                                           預設值為0對應的字元  是一個控制字元

     boolean[]                                     預設值為false

     String[]                                        預設值為null    

     7.Java是一種自由格式(free-form)的語言,是以空格、制表符、換行都不會影響程式的執行結果。

     8.類中包含兩種類型的元素:  成員和方法。

     成員可以是任意類型的對象,可以通過其引用與其通信;也可以是基本類型的一種。 如果字段是對某個對象的引用,那麼必須初始化該引用,以便使其與一個實際對象相關聯。

     若類的某個成員是基本資料類型,即使沒有初始化,Java也會確定它獲得一個預設值。

     方法的基本組成部分包括:名稱、參數、傳回值和方法體。方法名和參數清單唯一辨別一個方法。Java中方法隻能作為類的一部分來建立,方法隻有通過對象才能被調用(标注:static類型的方法針對類調用,不依賴于對象)。

     在參數清單中必須指定每個所傳遞對象的類型及名字,Java中任何傳遞對象的場合中,傳遞的實際上是引用。但是如果參數被設定為String類型,則必須傳遞一個String對象,否則編譯器将抛出錯誤。

     方法的傳回類型如果是void,return關鍵字的作用隻是用來退出方法。但是如果傳回類型不是void,無論在什麼地方傳回,編譯器都會強制傳回一個正确類型的傳回值。

     9.static關鍵字:當聲明一個事物是static時,意味着這個域或方法不會與包含它的那個類的任何對象執行個體關聯在一起。非靜态方法可以調用靜态方法并通路靜态變量,但是靜态方法不能調用非靜态方法。

     10.Java lang包是每個java檔案中都自動導入的一個包。

##### 第三章:操作符

     1.在最底層,Java中的資料是通過使用操作符來操作的。

     2.“+”操作符有時意味着字元串連接配接,并且如果必要,它還要執行“字元串轉換”,試着将其他類型元素轉換為String

     3.Random類可以生成随機數。 如果建立過程中沒有傳遞任何參數,那麼Java就會将目前時間作為随機數生成器的種子(seed參數),并由此在程式每一次執行時都要産生不同的輸出。

     通過在建立Random對象時提供種子(用于随機數生成器的初始化值,随機數生成器對于特定的種子值總是産生相同的随機數序列),就可以在每一次執行程式時都生成相同的随機數。  例如new Random(1).nextFloat();

     4. i++ 和 ++i 的差別

     i++ 在表達式運算完再對 i 進行加一操作(運算過程中,使用的是i的值)

     ++i 在表達式運算之前,先對i進行加一操作

     i++ 舉例:

     int i = 0;

     print(i++ + 1);          //輸出1

     print(i);                   //輸出1

     ++i 舉例:

     print(++i + 1);         //輸出2

     print(i);                  //輸出1

     5. == 和 != 比較的是對象的引用, .equals()方法比較的是對象的實際内容,但是.equals()方法不适用于基本類型。基本類型直接使用 == 和 !=

     6.自己定義的類中,如果不重寫equals()方法,equals()預設會比較兩個對象的引用。大多數Java類庫都實作了equals方法,以便于來比較對象的内容。

     7.邏輯操作符中的“短路”現象:一旦能夠明确無誤地确定整個表達式的值,就不再計算表達式的剩餘部分。

     8.按位操作符用來操作整數基本資料類型中的單個“比特”,即二進制位。按位操作符會對兩個參數中對應的位執行布爾代數運算,并最終生成一個結果。

     9.異或運算:個人了解: 兩位(bit)隻能有一個為真或者假  例如:真假美猴王??? (不存在兩個真猴王 是以都是1的時候,1^1 = 0)

     10.移位操作符操作的運算對象也是二進制的“位”。移位操作符隻能用來處理整數類型。(還沒看完)

     11.三元操作符:也稱為條件操作符,屬于操作符的一種。

     12.将 Float 或 Double 類型轉換為整型值時,總是對該數字進行截尾。如果想實作數學中四舍五入的效果,調用Math.round()方法。

##### 第四章:控制執行流程

      1.在Java的控制語句中,涉及到的關鍵字包括:if-else、while、do while、for、return、break以及選擇語句switch。  Java中不支援goto語句,但是可以進行類似goto那樣的跳轉,比起典型的goto,有了更多的限制。

      2.while 和 do while 的唯一差別就是do while中的語句至少會執行一次,即便第一次表達式就被計算為false。 而在while循環結構中,如果條件第一次就為false,那麼其中的語句根本不會執行。實際應用中,while 比 do while 更常用一些。

      3.逗号操作符:(注意不是逗号分隔符,逗号用作分隔符時用來分隔函數的不同參數),Java裡唯一用到逗号操作符的地方就是 for 循環的控制表達式。在控制表達式的初始化和步進控制部分,可以使用一系列由逗号分隔的語句,而且那些語句均會獨立執行。

       執行個體:

       for (int i = 0, j = i + 1; i < 5; i++ , j = i + 1){

             print(i + "  " + j)

       }

       4.Foreach 文法:Java SE5引入的一種新的更加簡潔的for文法,用于數組和容器。foreach還可以用于任何 Iterable 對象。

       5.return 關鍵字有兩方面的用途:一方面指定一個方法傳回什麼值(假設它沒有Void傳回值),另一方面它會導緻目前的方法退出,并傳回那個值。

       6.break 和 continue :在任何循環語句的主體部分,都可用 break 和 continue 控制循環流程。其中,break 用于強行退出循環,不執行循環中剩餘的語句。而 continue 則停止執行目前的循環,然後退回循環起始處,開始下一次循環。

       7. while(true) 和 for(;;) 是一回事,都表示無窮循環。

       8.break label; continue label; 能起到goto的效果。

       label:                //後邊不能加表達式 隻在循環語句之前起作用

       outer-Iteration{

              inner-Iteration{

                       //...

                       break;    (1)

                       continue;     (2)

                       continue label;       (3)

                       break label;       (4)

              }

       (1)處,break 中斷内部循環,回到外部循環。

       (2)處,continue 使執行點回到内部循環的起始處,繼續執行内部循環。

       (3)處,continue label 同時中斷内部循環和外部循環,直接轉到label處,随後繼續執行循環過程,但是是從外部循環開始。

       (4)處,break label 會中斷所有循環,并回到label處,但是不會重新執行循環。實際上是完全終止了兩個循環。

       9.在Java中需要使用标簽的唯一理由是有循環嵌套存在,而且想從多層嵌套中break 或者 continue。

       10.Java中,switch語句的case後, 在JDK1.7 之前隻能是int char類型的, JDK 1.7 之後,case後可以跟String類型。 (1.7之後已寫代碼驗證,可以接String類型)。 但是case關鍵字後不能跟Float類型的值。

##### 第五章:初始化與清理

       1.構造器是一個在建立對象時被自動調用的特殊方法,Java中才用了構造器,并且提供了垃圾回收器。對于不再使用的資源,垃圾回收器能自動将其釋放。

       2.建立對象時,如果類具有構造器,Java就會在對象可以被使用之前自動調用相應的構造器,進而保證了初始化的進行。

       3.由于構造器名稱必須和類名完全相同,是以“每個方法的首字母小寫”的編碼風格不适用于構造器。

       4.構造器是一種特殊類型的方法,它沒有傳回值。 與傳回值為void不同。對于空(void)傳回值,盡管方法本身不會自動傳回什麼,但仍可以選擇讓它傳回别的東西,構造器則不會傳回任何東西。

       5.每個重載的方法都必須有一個獨一無二的參數類型清單。如果兩個參數的類型不同,那它們的順序不同也屬于重載,但是一般情況下不這麼做,這會使代碼難以維護。

       方法的傳回值不能作為重載的依據。

       6.預設構造器(無參構造器)是沒有形參的構造器,它的作用是建立一個預設對象,如果類中沒有構造器,則編譯器會自動幫你建立一個預設構造器。

        7.調用一個方法時,編譯器做了一些幕後的工作。它把“所操作對象的引用”作為第一個參數傳給方法(内部發生)。由于這個引用是編譯器偷偷傳進去的,是以沒有辨別符可用。但是有個專門的關鍵字this,this關鍵字隻能在方法内部使用,表示對“調用方法的對象”的引用。  注意:如果在方法内部調用同一個類的另一個方法,不必使用this,直接調用即可。

        8.static方法就是沒有this,在static方法内部不能調用非靜态方法,但是非靜态方法可以調用靜态方法。

        9.假設對象獲得了一塊“特殊”的記憶體區域(不是通過new獲得),由于垃圾回收器隻知道釋放那些由new配置設定的記憶體,是以它不知道如何釋放對象的這塊“特殊”記憶體。為了應對這種情況,Java允許在類中定義一個名為finalize()的方法。

          工作原理(假定是這樣):一旦垃圾回收器準備好釋放對象占用的存儲空間,将首先調用其finalize()方法,并且在下一次垃圾回收動作發生時,才會真正回收對象占用的記憶體。

        10.Java中的對象可能 不 被垃圾回收,垃圾回收隻與記憶體有關。 也就是說使用垃圾回收器的唯一原因是為了回收程式不再使用的記憶體。是以對于垃圾回收有關的任何行為來說(尤其是finalize()方法),都必須和記憶體及其回收有關。不要過多地使用finalize()

        11.無論是“垃圾回收”還是“終結”,都不保證一定發生。如果Java虛拟機(JVM)并未面臨記憶體耗盡的情形,它是不會浪費時間去執行垃圾回收以恢複記憶體。

        12.Java中方法内部的局部變量,Java以編譯時錯誤的形式來保證變量被初始化。但是類的資料成員(成員變量),如果是基本類型的話,會保證有一個預設的初始值。在類裡定義一個對象引用時,如果不将其初始化,此引用會獲得一個特殊值null。

        13.在類的内部,變量定義的先後順序決定了初始化的順序。成員變量會在任何方法(包括構造器)被調用之前得到初始化。

        14.無論建立多少個對象,靜态資料都隻占用一份存儲區域。static不能應用于局部變量,是以它隻能作用于域。如果一個域是靜态的基本類型的域,且也沒有對它進行初始化,那麼它就會獲得基本類型的标準初值;如果它是一個對象引用,那麼它的預設初始值是null。

        15.靜态初始化隻有在必要時刻才會進行。初始化的順序是先靜态對象,而後是“非靜态”對象。

        16.Java允許将多個靜态初始化動作組織成一個特殊的“靜态子句”(有時也叫“靜态塊”)。與其他靜态初始化動作一樣,靜态塊僅執行一次:當首次生成這個類的一個對象時,或者首次通路屬于那個類的靜态資料成員時。

        17.枚舉(enum)類型的執行個體是常量,是以按照命名習慣它們都用大寫字母表示(如果一個名字中有多個單詞,用下劃線将它們隔開)。

        18.在建立enum時,編譯器會自動添加一些有用的特性。例如,會建立toString()方法,以便可以很友善地顯示某個enum執行個體的名字,編譯器還會建立ordinal()方法,用來表示某個特定enum常量的聲明順序,以及static values()方法,用來按照enum常量的聲明順序,産生由這些常量值構成的數組。enum是類,并且有自己的方法。

##### 第六章:通路權限控制

        1.當編寫一個Java源代碼檔案時,此檔案通常被稱為編譯單元,每個編譯單元必須有一個相同的字尾名.Java,而在編譯單元内則可以有一個public類,該類的名稱必須與檔案的名稱相同(包括大小寫,但是不包括檔案的字尾名),每個編譯單元隻能有一個public類。

        2.預設通路權限沒有任何關鍵字,但通常是指包通路權限(有時也表示為friendly)。這就意味着目前的包中的所有其他類,對那個成員都有通路權限,但對于這個包之外的所有類,這個成員卻是private。

        3.包通路權限允許将包内所有相關的類組合起來,以使它們彼此之間可以輕松地互相作用。當把類組織起來放進一個包内之時,也就給它們的包通路權限的成員賦予了互相通路的權限。

        4.取得對某成員的通路權的唯一途徑:

           >.使該成員為public,無論是誰,無論在哪,都可以通路該成員。

           >.通過不加通路權限修飾詞并将其他類放置于同一個包内的方式給成員賦予包通路權。于是包内的其他類就可以通路該成員了。

           >.繼承來的類既可以通路public成員,又可以通路protected成員(但是不能通路private成員)。

           >.提供通路器和編譯器(get/set方法),以讀取和改變數值。

        5.關鍵字private的意思是,除了包含該成員的類之外,其他任何類都無法通路這個成員。

        6.protected修飾的成員,在同一個包中,相當于預設包權限。在不同包下protected成員對其他非繼承類不可見。 繼承類可以通路 protected 成員和 public 成員。(書中原話:如果建立了一個新包,并自另一個包中繼承類,那麼唯一可以通路的成員就是源包中的public成員。  這個地方有些疑問???)

        7.通路權限的控制常被稱為是 具體實作的隐藏。把資料和方法包裝進類中,以及具體實作的隐藏,常共同被稱作是“封裝”。其結果是一個同時帶有特征和行為的資料類型。

        8.通路權限控制将權限的邊界劃在了資料類型的内部。其中有兩個原因:

           >. 第一個原因是要設定用戶端程式員可以使用和不可以使用的界限。可以在結構中建立自己的内部機制,而不必擔心用戶端程式員會偶然地将内部機制當作是他們可以使用的接口的一部分。

           >. 第二個原因是将接口和具體實作進行分離。如果結構是用于一組程式之中,而用戶端程式員除了可以向接口發送資訊之外(調用方法)什麼也不可以做的話,那麼就可以随意更改不是public的東西(例如有包通路權限,protect-ed和private成員),而不會破壞用戶端代碼。

        9.為了控制某個類的通路權限,修飾詞必須出現在class關鍵字之前。

           >.每個編譯單元(.java檔案)隻能有一個public類。這表示每個編譯單元都有單一的公共接口,用public類來表現。

           >.public類的名稱必須完全與含有該編譯單元的檔案名相比對,包括大小寫。

           >.編譯單元内完全不帶public類也是可能的。這種情況下可以随意對檔案命名。

        10.通常情況下,類的通路權限僅有 包通路權限 或 public。 但是特殊情況下(例如内部類可以是private或者protected,但是這是特例)。如果不希望其他任何人對該類擁有通路權限,可以把所有的構造器都指定為private。進而阻止任何人建立該類的對象。但是有一個例外,可以在該類的static成員内部進行建立。

        11.如果我們自己編寫了構造器,類就不會再自動建立構造器了。

##### 第七章:複用類

        1.複用類的兩種方法:

          >.第一種方法隻需在新的類中産生現有類的對象,由于新的類是由現有類的對象組成,是以這種方法稱為組合。

          >.第二種方法按照現有類的類型來建立新類。無需改變現有類的形式,采用現有類的形式并在其中添加新代碼。這種方式稱為繼承。繼承是面向對象程式設計的基石之一。

        2.每一個非基本類型的對象都有一個toString()方法。

        3.在初始化對象引用時,可以在代碼的下列位置進行:

           >.在定義對象的地方。這意味着它們總能在構造器被調用之前被初始化。

           >.在類的構造器中。

           >.在使用這些對象之前,這種方式稱為“惰性初始化”。在生成對象不必每次都生成對象的情況下,這種方式可以減少額外的負擔。

           >.使用執行個體初始化。(實力初始化在代碼中表現為:{}包裹的代碼塊在靜态塊之後,構造方法之前被調用)

        4.繼承并不隻是複制基類的接口,當建立了一個子類的對象時,該對象包含了一個基類的子對象。這個子對象和用基類直接建立的對象時一樣的。基類的子對象被包裝在子類對象内部。

        5.編譯器會預設為一個類提供無參構造方法。建立子類時,會先調用父類的構造方法。如果基類沒有預設的構造器或者想要調用一個帶參數的基類構造器,必須用關鍵字super顯式地編寫調用基類構造器的語句,并且加上适當的參數。注意:super()語句必須放在子類構造函數的第一行。

        6.除了記憶體以外,不能依賴垃圾回收器去做任何事情。如果需要清理,最好是編寫自己的清理方法,但不要使用finalize().

        7.組合技術通常用于想在新的類中使用現有類的功能而非它的接口這種情形。實作方式是在新的類中嵌入一個現有類的private對象。  使用者隻能調用新類的方法,但是在新類的方法内部實際上調用了現有類的方法來實作某些功能。

        示例:

        現有類 class A{

                        public void doSth(){};

                  }

         新類: class B{

                          private A a = new A();

                          public bMethod(){

                                   a.doSth();

                           }

                   }

          8.繼承技術中最重要的方面是用來表現子類和基類之間的關系。概括為:子類是父類的一種類型。

          9.由子類轉型成基類,一般稱為“向上轉型”。向上轉型是從一個較專用類型向通用類型轉換,是以總是安全的。子類可能比基類含有更多的方法,但是它必須至少具有基類中所含有的方法。

          10.final關鍵字在定義基本類型變量時表示基本類型的數值恒定不變,當final修飾對象引用時,表示對象引用恒定不變。一旦引用被初始化指向一個對象,就無法再把它改為指向另一個對象。但是對象自身是可以被修改的。

          11.一個既是static又是final的域隻占據一段不能改變的存儲空間。

          12.final修飾的方法在子類中可以使用,但是不可以被重寫。類中所有的private方法都隐式地指定為是final。由于子類無法通路基類的private方法,是以也就無法重寫基類的private方法。

          13.final修飾的類不能夠被繼承。final類中的所有方法都隐式地指定為是final的。

##### 第八章:多态(暫時略過)

          1.将一個方法調用同一個方法主體關聯起來被稱作綁定。 如果在程式執行前進行綁定,叫做前期綁定。當編譯器隻有一個基類的引用時,無法确定調用哪個方法。

          2.後期綁定: 運作時根據對象的類型進行綁定。後期綁定也叫作動态綁定或者運作時綁定。Java中除了static方法和final方法(private方法屬于隐式final方法)之外,其他方法都是後期綁定。

          3.如果某個方法是靜态的,那麼它的行為不具有多态性。子類的靜态變量和父類的靜态變量是占據不同存儲空間的。

          4.

##### 第九章:接口

          1.抽象方法僅有聲明,沒有方法體。包含抽象方法的類叫做抽象類,如果一個類包含一個或多個抽象方法,該類必須被限定為抽象的。

          2.如果從一個抽象類繼承,子類有兩種處理方式:

             >.實作基類中所有的抽象方法

             >.如果沒有實作基類所有的抽象方法,子類也需要定義為抽象類。

          3.使某個類成為抽象類不需要所有的方法都是抽象的,隻需要将某些方法聲明為抽象即可。

          4.抽象類中可以沒有抽象方法,并且抽象類中的非抽象方法可以有方法體。

          5.interface關鍵字産生一個完全抽象的類,類中的方法沒有任何的具體實作。它允許建立者确定方法名、參數清單和傳回類型,但是沒有任何方法體。接口隻提供形式,不提供具體實作。

          6.當要實作一個接口時,在接口中被定義的方法必須被定義為public。

          7.使用String類型的split()方法,可以作為一種建立數組的快捷方法。

             執行個體:

             String  word = "Hello,world,Hello,kotlin";

             String[]  arr = word.split(",");

          8.Java中接口是多繼承,需要将所有接口名都置于implements關鍵字後,用逗号将他們一一隔開。可以繼承任意多個接口,并可以向上轉型為每個接口,因為每個接口都是一個獨立類型。

          9.使用接口的核心原因:為了能夠向上轉型為多個基類型(以及由此帶來的靈活性)。使用接口的第二個原因和使用抽象類相同:防止用戶端程式員建立該類的對象,并確定這僅僅是建立一個接口。

          10.通過繼承,可以很容易地在接口中添加新的方法聲明,還可以通過繼承在新接口中組合多個接口。

          11.接口中定義的成員變量自動是static和final的(也是public的),是以接口成為了一種很便捷地建立常量組的工具。在Java SE5之前,這是産生和enum具有相同效果的類型的唯一途徑。

          12.接口中定義的域不能是“空final”,但是可以被非常量表達式初始化。例如:int RANDOM_INT = new Random().nextInt();

          13.接口中的域不是接口的一部分,它們的值被存儲在該接口的靜态存儲區内。

          14.接口可以嵌套在其他類或者其他接口中。在類中嵌套接口可以擁有public和“包通路”兩種可視性。

          15.接口也可以被實作為private的,private接口不能在定義它的類之外被實作。

##### 第十章:内部類

          1.可以将一個類的定義放在另一個類的定義内部,這就是内部類。内部類是一種非常有用的特性,它允許你把一些邏輯相關的類組織在一起,并控制位于内部的類的可視性。 内部類群組合是完全不同的概念。

          2.如果想從外部類的非靜态方法之外的任意位置建立某個内部類的對象,那麼必須具體地指明這個對象的類型。例如:OuterClassName.InnerClassName

          3.内部類可以通路外圍對象的所有成員,不需要任何特殊條件。内部類還擁有外圍類的所有元素的通路權。

          4.如果在内部類中想生成對外部類對象的引用,可以使用外部類的名字後面加.this。 例如Outer.this

          5.要想直接建立内部類的對象,必須使用外部類的對象進行建立。在外部類對象建立之前不可能建立内部類(非靜态)的對象。如果建立的是嵌套類(靜态内部類),則不需要對外部類對象的引用。

          6.内部類使用場景:當将内部類向上轉型為其基類,尤其是轉型為一個接口的時候。(從實作了某個接口的對象,得到對此接口的引用,與向上轉型為這個對象的基類,實質上效果是一樣的。) 這樣可以使某個接口的實作完全不可見,并且不可用。所得到的隻是指向接口或基類的引用,能夠友善地隐藏實作細節。

          7.如果希望一個匿名内部類,并且希望它使用一個在其外部定義的對象,那麼編譯器會要求其參數引用時final類型的。

          8.匿名類和正規的繼承相比有些受限,匿名類既可以擴充類,也可以實作接口,但是不能同時進行。并且如果實作接口,隻能實作一個。

          9.嵌套類:static修飾的内部類。此時,内部類和外部類的對象之間沒有聯系。

             >.要建立嵌套類的對象,不需要外部類的對象。

             >.不能從嵌套類的對象中通路非靜态的外部類對象。

             嵌套類和普通内部類的另一個差別:普通内部類的字段和方法,隻能放在類的外部層次上,普通内部類不能有static資料和static字段,也不能包含嵌套類。但是嵌套類可以包含這些東西。

             普通内部類不能包含static資料和方法的原因:可以把普通内部類看做外部類的非靜态成員,它的初始化必須在外部類對象建立以後才進行,是以要加載内部類,需要先執行個體化外部類對象。  而Java虛拟機要求所有靜态變量必須在對象建立完成之前完成,有沖突。

              普通内部類可以包含 static final 類型的常量:常量存放在記憶體中的常量池。它的機制和變量不同,加載常量不需要加載類。

          10.嵌套類可以作為接口的一部分。放到接口中的任何類都自動是public和static的。

          11.一個内部類被嵌套多少層不重要,它能透明地通路所有它嵌入的外圍類的所有成員。

          12.使用内部類最吸引人的原因是:每個内部類都能獨立地繼承自一個(接口的)實作,是以無論外圍類是否已經繼承了某個(接口的實作),對于内部類都沒有影響。

          13.内部類的一些特性:

              >.内部類可以有多個執行個體,每個執行個體都有自己的狀态資訊,并且與其外部類對象的資訊互相獨立。

              >.在單個外圍類中,可以讓多個内部類以不同的方式實作同一個接口,或繼承同一個類。

              >.建立内部類對象的時刻并不依賴于外圍類對象的建立。

              >.内部類是一個獨立的個體。

          14.局部内部類不能有通路說明符,它不是外圍類的一部分,但是它可以通路目前代碼塊内的常量,以及此外圍類的所有成員。

##### 第十一章:持有對象

          1.通常,程式根據運作時的條件才建立新的對象,但是有時不知道對象的數量及類型。Java中提供了容器類解決這個問題。

          2.容器的基本類型: List 、Set、 Map、 Queue。容器類可以自動地調整自己的尺寸。

          3.Java容器類類庫的用途是“儲存對象”,并且分為兩個不同的概念:

            >.Collection,一個獨立元素的序列,這些元素都服從一條或多條規則。List必須按照插入的順序儲存元素,Set不能有重複的元素,Queue按照排隊規則來确定對象産生的順序。

            >.Map,一組成對的“鍵值對”對象,允許使用鍵來查找值。

         4.Arrays.asList()方法接受一個 數組 或是 一個用逗号分隔的元素清單(使用可變參數),并将其轉換成一個List對象。 Collections.addAll()方法接受一個Collection對象,以及一個數組或者一個用逗号分隔的清單,将元素添加到Collection中。

         5.Collection.addAll()方法隻能接受一個Collection對象作為參數(結尾沒有s)。是以沒有Arrays.asList()或者Collections.addAll()靈活,這兩個方法都是用的可變參數清單。

         6.List以特定的順序儲存一組元素。Set中元素不能重複,Queue隻允許在容器的一端插入對象,并從另一端移除對象。Map中儲存的是鍵值對。

         7.List分為兩種: ArrayList 和 LinkedList

            >.ArrayList 擅長随機通路元素,但是在ArrayList中間插入和删除元素時較慢。

            >.LinkedList 擅長在List中間的插入和删除,但是在随機通路時相對較慢。

         8.疊代器是一個對象,它的作用是周遊并選擇序列中的對象,而使用者不需要知道序列底層的結構。 疊代器通常也被稱為輕量級對象:建立它的代價小。是以,對疊代器會有一些限制:

            >.Java的Iterator隻能單向移動,這個Iterator隻能用來:

               >.使用iterator()要求容器傳回一個Iterator

               >.使用next()獲得序列中的下一個元素。

               >.使用hasNext()檢查序列中是否還有元素。

               >.使用remove()方法将疊代器傳回的元素删除。

         9.Iterator可以移除由next()産生的最後一個元素,這意味着在調用remove()方法之前必須先調用next()方法。

         10.ListIterator 是Iterator的子類型,它隻能用于各種List類的通路。ListIterator可以雙向移動,它還可以産生相對于疊代器在清單中指向的目前位置的前一個和後一個元素的索引,并且可以使用set()方法替換它通路過的最後一個元素。

         11.“棧”通常是指“後進先出”(LIFO)的容器。有時棧也被稱為疊加棧,因為最後一個壓入棧的元素第一個彈出棧。Stack是用LinkedList實作的。

         12.Set不儲存重複的元素,Set最常被使用的是測試 “歸屬性”,可以很容易地查詢某個對象是否在某個Set中。 查找是Set中最重要的操作,HashSet對快速查找進行了優化。

         13.Queue是一個典型的先進先出(FIFO)的容器,即從容器的一端放入事物,從另一端取出,并且放入容器的順序和取出的順序是相同的。  隊列常被當成一種可靠的将對象從程式的某個區域傳遞到另一個區域的途徑。

             LinkedList提供了方法以支援隊列的行為,并且它實作了Collection接口, 是以LinkedList可以用作Queue的一種實作。

         14.PriorityQueue 優先級隊列聲明下一個彈出元素是最需要的元素(具有最高的優先級)。當在PriorityQueue上調用offer()方法插入一個對象時,這個對象會在隊列中被排序。預設的排序将使用對象在隊列中的自然順序,但是可以通過提供自己的Comparator來修改這個順序。PriorityQueue可以確定擷取的元素是隊列中優先級最高的元素。

         15.PriorityQueue中允許重複,最小的值擁有最高的優先級(如果是String,空格也可以算值,并且比字母的優先級高)。

##### 第十二章:通過異常處理錯誤

         1.異常情形是指阻止目前方法或作用域繼續執行的問題。 與普通問題的差別:普通問題是指,在目前環境下能得到足夠的資訊,總能處理這個錯誤。而異常情形,不能繼續下去,因為在目前環境無法獲得必要的資訊來解決問題。 能做的事情是 抛出異常(從目前環境跳出,把問題交給上一級環境)。

         2.抛出異常後,将會使用new在堆上建立異常對象。然後目前的執行路徑被終止 ,并且從目前環境中彈出對異常對象的引用。此時異常處理機制接管程式,并且尋找一個恰當的地方繼續執行程式。這個恰當的地方就是異常處理程式,它的任務是将程式從錯誤狀态中恢複,以使程式要麼換一種方式運作,要麼繼續運作下去。

         3.能夠抛出任意類型的Throwable對象,它是異常類型的根類。通常,對于不同類型的錯誤,要抛出相應的異常。錯誤資訊可以儲存在異常對象内部或者用異常類的名稱來暗示,上一層環境通過這些資訊來決定如何處理異常。

         4.try塊:如果在方法内部抛出了異常(或者方法内部調用的其他方法抛出了異常),這個方法将在抛出異常的過程中結束。要是不希望方法就此結束,可以在方法中設定一個特殊的塊來捕獲異常。

         5.異常處理程式緊跟在try塊後,以關鍵字catch表示。當異常被抛出時,異常處理機制負責搜尋參數與異常類型相比對的第一個處理程式。然後進入catch語句執行,一旦catch子句結束,則處理程式的查找過程結束。

         6.可以隻寫一個異常處理程式來捕獲所有類型的異常。通過捕獲異常類型的基類Exception,就可以做到這一點。這将捕獲所有異常,是以最好把Exception基類放到處理程式清單的末尾,防止它搶在其他處理程式之前把異常捕獲。

         7.printStackTrace()方法所提供的資訊可以通過getStackTrace()方法來直接通路,這個方法将傳回一個由棧軌迹中的元素所構成的數組,其中每個元素都表示棧中的一幀。元素0是棧頂元素,并且是調用序列中的最後一個方法調用(這個Throwable被建立和抛出之處)。數組中的最後一個元素和棧底是調用序列中的第一個方法調用。

         8.重新抛出異常會把異常抛給上一級環境中的異常處理程式,同一個try塊和後續catch語句會被忽略。此外,異常對象的所有資訊都得以保持,是以高一級環境中捕獲此異常的處理程式可以從這個異常對象中擷取所有資訊。

##### 第十三章:字元串

         1.String對象是不可變的,每一個看起來會修改String值的方法,實際上都是建立了一個新的String對象,用來包含修改後的字元串内容。

         2.String對象具有隻讀特性,是以指向它的任何引用都不能改變它的值,是以也不會對其他引用造成影響。

         3.用于String的 “+” 和 “+=”是Java中僅有的兩個重載過的操作符,Java不允許程式員重載任何操作符。

         4.StringBuilder允許指定大小,預先指定StringBuilder的大小可以避免多次重新配置設定緩沖。當為一個類編寫toString()方法時,如果字元串操作比較簡單,使用String。但是如果要在toString()方法中使用循環,最好建立一個StringBuilder對象。

         5.StringBuilder是非線程安全的,StringBuffer是線程安全的。

         6.    正規表達式(略過,一塊大肉)

 ##### 第十四章:類型資訊

         1.面向對象程式設計中的基本目的是:讓代碼隻操縱基類的引用。這樣,如果添加一個新類來擴充程式,就不會影響到原來的代碼。子類中重寫的方法是被動态綁定的,是以即使通過父類的引用來調用,也可以産生正确的行為,這就是多态。

         2.RTTI(Run-Time Type Information):運作時類型資訊。在Java中,所有的類型轉換都是在運作時進行正确性檢查的。

         3.類型資訊在運作時的表示工作由Class對象的特殊對象完成,它包含了與類有關的資訊。

         4.類是程式的一部分,每個類都有一個Class對象。每當編寫并編譯了一個新類,就會産生一個Class對象(确切的說,是被儲存在一個同名的.class檔案中)。為了生成這個類的對象,運作這個程式的Java虛拟機(JVM)将使用被稱為“類加載器”的子系統。

         5.類加載器子系統實際上可以包含一條類加載器鍊,但是隻有一個原生類加載器,它是JVM實作的一部分。原生類加載器加載的是可信類,包括Java API類,它們通常是在本地加載的。

         6.所有的類都是在對其第一次使用時,動态加載到JVM中的。當程式建立第一個對類的靜态成員的引用時,就會加載這個類。這個證明構造器也是類的靜态方法,即使在構造器之前并沒有使用static關鍵字。是以,使用new 操作符建立類的新對象也會被當做類的靜态成員的引用。

         7.Java程式在它開始運作之前并非被完全加載,其各個部分是在必需時才加載的。類加載器首先檢查這個類的Class對象是否已經加載。如果尚未加載,預設的類加載器就會根據類名查找.class檔案。在這個類的位元組碼被加載時,它們會接受驗證,以確定其沒有被破壞,并且不包含不良Java代碼(這也是Java中用于安全防範目的的措施之一)。

         8.Class對象僅在需要的時候才被加載,static初始化是在類加載時進行的。

         9.Class.forName("")方法。 forName()方法是取得Class對象的引用的一種方法,傳回一個Class對象的引用。調用forName()方法時候,如果類還沒有進行加載,就會加載類(并且會初始化對象)。getClass()方法可以擷取Class引用。注意:在傳遞給forName()的字元串中,必須使用全限定名(包含包名的類名)。

         10.為了使用類做的三個準備工作:

             >.加載,由類加載器執行。該步驟将查找位元組碼(通常在classpath所指定的路徑中查找,但這是非必需的),并從這些位元組碼中建立一個class對象。

             >.連結。在連結階段将驗證類中的位元組碼,為靜态域配置設定存儲空間,并且如果必須的話,将解析這個類建立的對其他類的所有引用。

             >.初始化。如果該類具有父類,則對父類初始化,執行靜态初始化器和靜态初始化塊。目前類的初始化被延遲到了對靜态方法或者非常數靜态域進行首次引用時才執行。

         11.Java提供了另一種方法來生成對Class對象的引用,即使用類字面常量。例如A.class。 這樣做不僅簡單,而且更安全,在編譯時就會受到檢查。也更高效。  類字面常量不僅可以應用于普通的類,還可以應用于接口、數組以及基本資料類型。  對于基本資料類型的包裝器類,還有一個标準字段TYPE。TYPE字段是一個引用,指向對應的基本類型的Class對象。例如 Byte.TYPE     注意: 當使用.class來建立對象的引用時,不會自動初始化該Class對象。

         12.目前已知的RTTI形式包括:

             >.傳統的類型轉換,由RTTI確定類型轉換的正确性,如果執行了一個錯誤的類型轉換,會抛出ClassCastException異常。

             >.代表對象的類型的Class對象。通過查詢Class對象可以擷取運作時所需的資訊。

             >.RTTI 在 Java中的第三種形式:關鍵字instanceOf,傳回一個布爾值,告訴我們對象是不是某個特定類型的執行個體。instanceOf隻可以将引用與命名類型比較,不能與Class對象比較。

         13.interface關鍵字的一種重要目标就是允許程式員隔離構件,進而降低耦合性。

#####   第十五章:泛型

          1.泛型的主要目的之一就是用來指定容器中要持有什麼類型的對象,而且由編譯器來保證類型的正确性。

          2.類型參數:用尖括号包覆,放在類名後邊。在使用這個類的時候用實際的類型替換此類型參數。

          3.元組:将一組對象直接打包存儲于其中的一個單一對象,這個容器對象允許讀取其中元素,但是不允許向其中放入新的對象。(這個概念也稱為資料傳送對象)。通常,元組可以具有任意長度,同時,元組中的對象可以是任意不同的類型。注意:元組隐含地保持了其中元素的次序。

          4.泛型也可以應用于接口。例如 生成器,這是一種專門負責建立對象的類。實際上這是工廠方法設計模式的一種應用。不過,當使用生成器建立新的對象時,它不需要任何參數,而工廠方法一般需要參數。

          5.Java泛型中的一個局限性:基類類型無法作為類型參數。不過Java SE5 具備了自動打包和拆包的功能,可以很友善的在基本類型和其相應的包裝器類之間進行轉換。

          6.泛型方法所在的類可以是泛型類,也可以不是泛型類。是否擁有泛型方法,與其所在的類是否是泛型沒有關系。  如果使用泛型方法可以取代将整個類泛型化,那麼就應該隻使用泛型方法。 對于一個static方法,無法通路泛型方法的類型參數,如果static方法需要使用泛型的類型參數,就必須使其成為泛型方法。

          7.由于擦除的原因,将泛型應用于異常時非常受限的。catch語句不能捕獲泛型類型的異常,因為在編譯期和運作時都必須知道異常的确切類型。泛型類也不能直接或間接繼承自Throwable。但是,類型參數可能會在一個方法的throws語句中用到,使得可以編寫随檢查型異常的類型而發生變化的泛型代碼。

##### 第十六章:數組

          1.數組可以通過整型索引值通路它們的元素,并且它們的尺寸不能改變。

          2.數組和其他種類的容器之間有三個差別:效率、類型和儲存基本類型的能力。在Java中數組是一種效率最高的存儲和随機通路對象引用序列的方式。數組就是一個簡單的線性序列,這使得元素通路非常快速,付出的代價是數組大小固定,并且在生命周期中不可變。 ArrayList的效率比數組低很多。

          3.無論使用哪種類型的數組,數組辨別符其實隻是一個引用,指向在堆中建立的一個真實對象,這個數組對象用以儲存指向其他對象的引用。可以作為數組初始化文法的一部分隐式地建立此對象,或者用new表達式顯示地建立。隻讀成員length是數組對象的一部分(事實上,這是數組對象唯一可通路的字段或者方法),表示此數組對象可以存儲多少元素。"[]"文法是通路數組對象的唯一方式。

          4.新生成一個數組對象時,其中所有的引用被自動初始化為null,是以檢查其中的引用是否為null,即可知道數組的某個位置是否有對象。  基本類型的數組如果是數值型的,就被自動初始化為0,如果是字元型的,就被自動初始化為(char)0,如果是布爾類型的,就被初始化為false。

          5.基本類型數組和對象數組一樣,隻不過基本類型的數組直接存儲基本類型資料的值。

          6.JavaSe5中,Arrays.deepToString()方法,它可以将多元數組轉換為多個String。

          7.通常,數組與泛型不能很好地結合。不能執行個體化具有參數化類型的數組。擦除會移除參數類型資訊,而數組必須知道它們所持有的确切類型,以強制保證類型安全。

          8.使用Arrays.fill()可以填充整個數組,或者隻填充數組的某個區域。但是由于隻能用單一的值來調用Arrays.fill(),是以所産生的結果并非特别有用。

          9.Arrays的實用方法: 其中六個基本方法:equals()用于比較兩個數組是否相等(deepEquals()用于多元數組)。fill()用于填充數組,sort()用于對數組排序。binarySearch()用于在已排序的數組中查找元素,toString()産生數組的String表示,hashCode()産生數組的散列碼,所有這些方法對各種基本類型和Object類重載過。Arrays.asList()接受任意的序列或數組作為其參數,并将其轉換為List容器。

          10.Java标準類庫中提供有static方法System.arraycopy(),用它複制數組比用for循環要快很多。System.arraycopy()針對所有類型做了重載。  System.arraycopy()不會執行自動包裝和自動拆包。

          11.基本類型數組和對象數組都可以複制。如果複制對象數組,那麼隻是複制了對象的引用——而不是對象本身的拷貝。這被稱作淺複制(shallow copy)。

          12.Arrays類提供了重載後的equals()方法,用來比較整個數組。同樣此方法針對所有基本類型與Object都做了重載。數組相等的條件是元素個數必須相等,并且對應位置的元素也相等。

          13.程式設計的基本目标是“将保持不變的事物與會發生改變的事物相分離”,通過使用“政策”,可以将“會發生變化的代碼”封裝在單獨的類中(政策對象),你可以将政策對象傳遞給總是相同的代碼,這些代碼将使用政策來完成其算法。

          14.Java有兩種方式來提供比較功能。第一種是實作java.lang.Comparable接口,使類具有“天生的”比較能力,這個接口隻有一個compareTo()方法。這個方法接收一個Object類型的參數,如果目前對象小于參數則傳回負值,如果相等則傳回0,如果目前對象大于參數則傳回正數。

           15.數組排序:使用内置的排序算法,就可以對任意的基本類型數組排序,也可以對任意的對象數組進行排序,隻要該對象實作了Comparable接口或具有相關聯的Comparator。

           16.String排序算法依據詞典編排順序排序,是以大寫字母開頭的詞都放在前面輸出,然後才是小寫字母開頭的詞。如果想忽略大小寫字母将單詞都放在一起排序,那麼可以使用String.CASE_INSENSTIVE_ORDER.

           17.Java标準類庫中的排序算法針對正排序的特殊類型進行了優化——針對基本類型設計的“快速排序”,以及針對對象設計的“穩定歸并排序”,是以無需擔心排序的性能,除非你可以證明排序部分的确是程式效率的瓶頸。

##### 第十七章:容器深入探究

           1.Set是自己維護内部順序的(這使得随機通路變得沒有意義),如果想檢查Collection中的元素,必須使用疊代器。

           2.如果一個操作是未獲支援的,那麼在實作接口的時候可能導緻UnsupportedOperationException異常,而不是将産品程式交給客戶以後才出現異常。

           3.Set:存入Set的每個元素必須是唯一的,因為Set不儲存重複元素。加入Set的元素必須定義equals()方法以確定對象的唯一性。Set與Collection有完全一樣的接口,Set接口不保證維護元素的次序。

           4.HashSet:為快速查找設計的Set,存入HashSet的元素必須定義hashCode()。  如果沒有其他限制,應該預設選擇使用HashSet,它對速度進行了優化。

           5.TreeSet:保持次序的Set,底層為樹結構。使用它可以從Set中提取有序的序列。元素必須實作Comparable接口。

            6.LinkedHashSet:具有HashSet的查詢速度,且内部使用連結清單維護元素的順序(插入的次序)。于是在使用疊代器周遊時,結果會按元素插入的次序顯示。元素也必須定義hashCode()方法。

            7.必須為散列存儲和樹型存儲都建立一個equals()方法,但是hashCode()隻有在這個類将會被置為HashSet或者LinkedHashSet中時才是必須的。對于良好的程式設計風格而言,應該在重寫equals()方法時,總是同時重寫hashCode()方法。

            8.SortedSet中的元素可以保證處于排序狀态。Comparator comparator()傳回目前Set使用的Comparator,或者傳回null,表示以自然方式排序。

               >.Object first() 傳回容器的第一個元素。

               >.Object last() 傳回容器的最末一個元素。

               >.SortedSet  subSet(fromElement, toElement) 生成此Set的子集,範圍從fromElement(包含)到toElement(不包含)。

               >.SortedSet  headSet(toElement) 生成此Set的子集,由小于toElement的元素組成。

               >.SortedSet  tailSet(fromElement) 生成此Set的子集,由大于或等于fromElement的元素組成。

            9.除了并發應用,Queue在JavaSe 5中僅有的兩個實作是LinkedList和PriorityQueue,它們的差異在于排序行為而不是性能。 除了優先級隊列,Queue将精确地按照元素被置于Queue中的順序産生它們。

            10.雙向隊列允許在任何一端添加或移除元素。

            11.Map中的基本方法是put()和get(),但是為了容易顯示,toString()方法被重寫為可以列印鍵值對。get()方法使用的可能是能想象到的效率最差的方法來定位值,從數組的頭部開始,使用equals()方法依次比較鍵。但是這裡的關鍵是簡單。

            12.性能是Map中的一個重要問題,當在get()中使用線性搜尋時,執行速度會相當的慢,而這正是HashMap提高速度的地方。HashMap使用了特殊的值,稱作散列碼,來取代對鍵的緩慢搜尋。散列碼是“相對唯一的”、用以代表對象的int值,它是通過将該對象的某些資訊進行轉換而生成的。hashCode()是根類Object中的方法,是以所有Java對象都能産生散列碼,HashMap就是使用對象的hashCode()進行快速查詢的,這個方法能夠快速提高性能。

            13.HashMap :Map基于散清單的實作(它取代了HashTable)。插入和查詢“鍵值對”的開銷是固定的。可以通過構造器設定容量和負載因子,以調整容器的性能。

            14.LinkedHashMap: 類似于HashMap,但是疊代周遊時,取得“鍵值對”的順序是其插入次序,或者是最近最少使用(LRU)的次序。隻比HashMap慢一點,而在疊代通路時更快,因為它使用連結清單維護内部次序。

            15.TreeMap: 基于紅黑樹的實作。 檢視“鍵”或“鍵值對”時,它們會被排序,(次序由Comparable或Comparator決定)。TreeMap的特點在于,所得到的結果是經過排序的。TreeMap是唯一帶有subMap()方法的Map,它可以傳回一個子樹。

            16.WeakHashMap: 弱鍵(weak key)映射,允許釋放映射所指向的對象;這是為解決某類特殊問題而設計的。如果映射之外沒有引用指向某個“鍵”,則此“鍵”可以被垃圾回收器回收。

            17.ConcurrentHashMap: 一種線程安全的Map ,它不涉及同步加鎖。

            18.IdentityHashMap : 使用 == 代替equals()對鍵進行比較的散列映射。散列是映射中存儲元素時最常用的方式。

             19.SortedMap:使用SortedMap(TreeMap是其現階段的唯一實作),可以確定鍵處于排序狀态。這使得它具有額外的功能,這些功能由SortedMap接口中的下列方法提供:

                >.Comparator comparator(): 傳回目前Map使用的Comparator 或者 傳回 null,表示以自然方式排序。

                >.T firstKey() 傳回Map中的第一個鍵。

                >.T lastKey() 傳回Map中的最末一個鍵。

                >.SortedMap subMap(fromKey, toKey) 生成此Map的子集,範圍由fromKey(包含),到toKey(不包含)的鍵确定。

                >.SortedMap headMap(toKey)生成此Map的子集,由鍵小于toKey的所有鍵值對組成。

                >.SortedMap tailMap(fromKey) 生成此Map的子集,由鍵大于或等于fromKey的所有鍵值對組成。

            20.為了提高速度,LinkedHashMap散列化所有的元素,但是在周遊鍵值對時,卻又以元素的插入順序傳回鍵值對。此外,可以在構造器中設定LinkedHashMap,使之采用基于通路的最近最少使用(LRU)算法,于是沒有被通路過的(可被看做要删除的)元素就會出現在隊列的前面。對于需要定期清理元素以節省空間的程式來說,此功能使得程式很容易得以實作。

            21.正确的equals()方法必須滿足五個條件:

               >.自反性。 對任意x, x.equals(x) 一定傳回true。

               >.對稱性。 對任意x和y ,如果y.equals(x)傳回true,則x.equals(y)也傳回true。

               >.傳遞性。對任意x、y、z 如果x.equals(y)傳回true,y.equals(z) 傳回true, 則 x.equals(z)一定傳回true。

               >.一緻性。對任意x和y,如果對象中用于等價比較的資訊沒有改變,那麼無論調用x.equals(y)多少次,傳回的結果應該保持一緻,要麼一緻是true,要麼一直是false。

               >.對任何不是null的x,x.equals(null)一定傳回false。

           22.使用散列的目的在于:想要使用一個對象來查找另一個對象。不過使用TreeMap或者自己實作的Map也可以達到這個目的。

           23.Map.entrySet()方法必須産生一個Map.Entry對象集。但是Map.Entry是一個接口,用來描述依賴于實作的結構,是以如果想要建立自己的Map類型,必須同時定義Map.Entry實作。entrySet()使用了HashSet來儲存鍵值對。

            24.散列的價值在于速度,散列使得查詢得以快速進行。由于瓶頸位于鍵的查詢速度,是以解決方案之一就是保持鍵的排序狀态,然後使用Collections.binarySearch()進行查詢。

            25.散列将鍵儲存在某處,以便能夠很快找到。存儲一組元素最快的資料結構是數組,是以使用它來表示鍵的資訊(注意是鍵的資訊,而不是鍵本身)。但是因為數組不能調整容量,是以就有一個問題:我們希望在Map中儲存數量不确定的值,但是如果鍵的數量被數組容量限制的話,解決方法是:數組并不儲存鍵本身。而是通過鍵對象生成一個數字,将其作為數組的下标。這個數字叫做散列碼。

           26. ArrayList底層由數組支援,LinkedList是由雙向連結清單實作的,其中的每個對象包含資料的同時還包含指向連結清單中前一個與後一個元素的引用。如果經常在表中插入或删除元素,LinkedList比較合适。否則應該使用ArrayList。

           27.可以根據需要選擇TreeSet、HashSet或者LinkedHashSet。  HashSet 的性能基本上總是比TreeSet好,特别是在添加和查詢元素時,而這兩個操作也是最重要的操作。TreeSet存在的唯一原因是它可以維持元素的排序狀态。是以,隻有當需要一個排好序的Set時,才應該使用TreeSet。因為其内部結構支援排序,并且因為疊代是我們更有可能執行的操作,是以用TreeSet疊代通常比HashSet更快。

           28.除了IdentityHashMap,所有的Map實作的插入操作都會随Map尺寸的變大而明顯變慢。但是查找的代價通常要比插入小得多。   TreeMap通常要比HashMap慢。與使用TreeSet一樣,TreeMap是一種建立有序清單的方式。樹的行為是:總是保證有序,并且不必進行特殊的排序。一旦填充了一個TreeMap,就可以調用keySet()方法來擷取鍵的Set視圖,然後調用toArray()來産生由這些鍵構成的數組。

           29.當使用Map時,第一選擇應該是HashMap,隻有在你要求Map始終保持有序時,才需要使用TreeMap。

           30.LinkedHashMap在插入時比HashMap慢一點,因為它維護散列資料結構的同時還要維護連結清單(以保持插入順序)。正是由于這個連結清單,使得其疊代速度更快。

           31.HashMap的性能因子: 我們可以通過手工調整HashMap來提高其性能,進而滿足我們特定應用的需求。涉及到以下一些術語:

             >.容量:表中的桶位數。

             >.初始容量:表在建立時所擁有的桶位數。HashMap和HashSet都具有允許指定初始容量的構造器。

             >.尺寸:表中目前存儲的項數。

             >.負載因子:尺寸/容量  空表的負載因子是0,半滿表的負載因子是0.5,負載輕的表産生沖突的可能性比較小,是以對于插入和查找都是最理想的。HashMap和HashSet都具有允許指定負載因子的構造器,表示當負載情況達到該負載因子的水準時,容器将自動增加其容量(桶位數),實作方式是使容量大緻加倍,并重新将現有對象分布到新的桶位中(再散列)。

             >.HashMap使用的預設負載因子是0.75(隻有當表達到3/4滿時,才進行再散列),這個因子在時間和控件代價之間達到了平衡。更高的負載因子可以降低表所需的空間,但是會增加查找代價,查找是我們大多數時間做的操作。

           32.對象是可獲得的,是指此對象可在程式中的某處找到。如果一個對象是可獲得的,垃圾回收器就不能釋放它,因為它仍然為你的程式所用。如果一個對象不是“可獲得”的,那麼你的程式将無法使用它,是以将其回收是安全的。

           33.如果想繼續持有某個對象的引用,希望以後還能通路到該對象,但是也希望能夠允許垃圾回收器釋放它,這時應該使用Reference對象。這樣,你可以繼續使用該對象,而在記憶體耗盡的時候又允許釋放該對象。以Reference對象作為你和普通引用之間的媒介(代理),一定不能有普通的引用指向那個對象,這樣就能達到上述目的。普通引用是指沒有經過Reference對象包裝過的引用。

              SoftReference、WeakReference和PhantomReference由強到弱排列,對應不同級别的“可獲得性”。SoftReference用以實作記憶體敏感的高速緩存。 WeakReference是為實作“規範映射”而設計的,它不妨礙垃圾回收器回收映射的“鍵”或“值”。“規範映射”中對象的執行個體可以在程式的多處被同時使用,以節省存儲空間。Plantomreference用以排程回收前的清理工作,它比Java終止機制更靈活。

               使用SoftReference和WeakReference時,可以選擇是否将它們放入ReferenceQueue(用作回收前清理工作的工具)。而PhantomReference隻能依賴于ReferenceQueue。

           34.Vector是唯一可以自我擴充的序列,在Java1.0/1.1中被大量使用。Enumeration接口比Iterator小。

##### 第十八章:Java I/O系統

            1.File(檔案)既能代表一個特定檔案的名稱,又能代表一個目錄下的一組檔案的名稱。如果它指的是一個檔案集,我們就可以對此集合調用list()方法,這個方法會傳回一個字元數組。

            2.Arrays.sort(arr,String.CASE_INSENSITIVE_ORDER),将數組按字母順序排序,忽略大小寫。

            3.File類不僅僅隻代表存在的檔案或者目錄。也可以用File對象來建立新的目錄或者尚不存在的整個目錄路徑。我們還可以檢視檔案的特性(例如:大小、最後修改日期、讀/寫),檢查某個File對象代表的是一個檔案還是一個目錄,并可以删除檔案。

            4.流代表任何有能力産出資料的資料源對象或者是有能力接收資料的接收端對象。“流”屏蔽了實際的I/O裝置中處理資料的細節。Java中I/O類分成輸入和輸出兩部分,通過繼承,任何自InputStream或Reader派生而來的類都含有名為read()的基本方法,用于讀取單個位元組或者位元組數組。  任何自OutputStream 或 Writer派生而來的類都含有名為write()的基本方法,用于寫單個位元組或者位元組數組。 但是通常我們不會用到這些方法,它們之是以存在是因為别的類可以使用它們,以便提供更有用的接口。

            5.在Java1.0中,類庫的設計者限定與輸入有關的類都應該從InputStream繼承,與輸出有關的類都應該從OutputStream繼承。

            6.InputStream的作用是用來表示那些從不同的資料源産生輸入的類。 這些資料源包括:位元組數組、String對象、檔案、“管道”(工作方式與實際管道相似,從一端輸入,從另一端輸出)、一個由其他種類的流組成的序列(以便我們可以将它們收集合并到一個流内)、其他資料源(如Internet連接配接等)。

             每一種資料源都有相應的InputStream子類。FilterInputStream也屬于一種InputStream,為“裝飾器”類提供基類,其中,裝飾器可以把屬性或有用的接口與輸入流連接配接在一起。

             7.OutputStream類型的類決定了輸出所要去往的目标:位元組數組(但不是String)、檔案或管道。

             8.InputStream和OutputStream是面向位元組形式的I/O的。Reader和Writer提供相容Unicode與面向字元的I/O功能。

             9.有時我們必須把來自于“位元組”層次結構中的類和“字元”層次結構中的類結合起來使用。為了實作這個目的,要用到“擴充卡”類:InputStreamReader可以把InputStream轉換為Reader, OutputStreamWriter 可以把OutputStream轉換為Writer。

             10.無論我們何時使用readLine(),都不應該使用DataInputStream(編譯器會報錯),而應該使用BufferedReader。除了這一點,DataInputStream仍是I/O類庫中的首選成員。

              11.FileWriter對象可以向檔案寫入資料。建立與指定檔案連接配接的FileWriter,我們通常會用BufferedWriter将其包裝起來用以緩沖輸出(嘗試移除此包裝來感受對性能的影響——緩沖往往能顯著地增加I/O操作的性能)。

              12.System.out事先被包裝成printStream對象,System.err也被包裝成printStream對象,System.in是一個沒有被包裝過的未經加工的InputStream。

              13.Java的對象序列化将那些實作了Serializable接口的對象轉換成一個位元組序列,并能夠在以後将這個位元組序列完全恢複為原來的對象。利用對象序列化可以實作輕量級持久性。

              14.對象序列化的概念主要是為了支援兩種主要特性:

                 >.Java的遠端方法調用,它使存活于其他計算機上的對象使用起來就像是存活在本機上一樣。當向遠端對象發送消息時,需要通過對象序列化來傳輸參數和傳回值。

                 >.對Java Bean來說,對象的序列化也是必須的,使用一個bean時,一般情況下是在設計階段對它的狀态資訊進行配置。這種狀态資訊必須儲存下來,并在程式啟動時進行後期恢複。這種具體工作就是由序列化完成的。

##### 第十九章:枚舉類型

              1.關鍵字enum可以将一組具名的值的有限集合建立為一種新的類型,而這些具名的值可以作為正常的程式元件使用這是一種非常有用的功能。

              2.調用enum的values()方法,可以周遊enum執行個體。values()方法傳回enum執行個體的數組,而且該數組中的元素嚴格保持其在enum中聲明時的順序,是以可以在循環中使用values()傳回的數組。

              3.調用enum的ordinal()方法傳回一個int值,這是每個enum執行個體在聲明時的次序,從0開始。可以使用==來比較enum執行個體,編譯器會自動提供equals()方法和hashCode()方法。Enum類實作了Comparable接口,是以它具有compareTo()方法。同時,它還實作了Serializable接口。

              4.除了不能繼承自一個enum之外,我們基本上可以将enum看作一個正常的類。也就是說,我們可以向enum中添加方法,enum中甚至可以有main方法。

              5.在switch語句中使用enum,是enum提供的一項非常便利的功能。在switch中一般隻能使用整數值(JDK1.7之後,可以跟String類型),而枚舉執行個體天生就具備整數值的次序,并且可以通過ordinal()方法取得其次序(編譯器幫我們做了類似的工作),是以我們可以在switch語句中使用enum。

              6.enum中values()方法是由編譯器添加的static方法。

              7.所有的enum都是繼承自java.lang.Enum類,Java不支援多繼承,是以,enum不能再繼承其他類,但是在建立新的enum時,可以同時實作多個接口。

              8.JavaSE 5中引入了EnumSet,是為了通過enum建立一種替代品,以替代傳統的基于int的“位标志”。這種标志可以用來表示某種“開關”資訊,不過,使用這種标志,我們最終操作的隻是一些bit,而不是這些bit要表達的概念,是以很容易寫出讓人難了解的代碼。使用EnumSet的優點是:它在說明一個二進制位是否存在時,具有更好的表達能力,并且無需擔心性能。

              9.EnumSet的基礎是long,一個long值有64位,而一個enum執行個體隻需一位bit表示其是否存在,也就是說,在不超過一個long的表達能力的情況下,EnumSet最多可以應用于不超過64個元素的enum。  當EnumSet應用于多過64個元素的enum時,必要時會增加一個long。

              10.EnumMap是一種特殊的Map,它要求其中的鍵(key)必須來自一個enum,由于enum本身的限制,是以EnumMap内部可以由數組實作。是以EnumMap的速度很快, 我們可以放心地使用enum執行個體在EnumMap中進行查找操作。但是我們隻能将enum的執行個體作為鍵來調用put()方法,其他操作和一般Map差不多。

              11.枚舉類型非常适合用來建立狀态機。一個狀态機可以具有有限個特定的狀态,它通常根據輸入,從一個狀态轉移到下一個狀态,不過也可能存在瞬時狀态,而一旦任務執行結束,狀态機就會立刻離開瞬時狀态。

##### 第二十章:注解

               1.注解(也稱為中繼資料)為我們在代碼中添加資訊提供了一種形式化的方法,使我們可以在稍後某個時刻非常友善地使用這些資料。

               2.注解的文法比較簡單,除了@符号的使用之外,它基本與Java固有的文法一緻。JavaSE5中内置了三種,定義在java.lang中的注解:

                >.Override,表示目前的方法定義将覆寫父類中的方法。如果不小心拼寫錯誤,或者方法簽名對不上被覆寫的方法,編譯器會發出錯誤提示。

                >.Deprecated,如果程式員使用了注解為它的元素,那麼編譯器會發出警告資訊。

                >.SuppressWarnings,關閉不當的編譯器警告資訊。在Java SE5之前的版本中也可以使用該注解,不過會被忽略不起作用。

               3.注解的定義看起來像接口的定義,注解也會編譯成class檔案。  除了@符号以外,注解的定義很像一個空的接口。定義注解時,會需要一些元注解,如@Target 和 @Retention。 @Target用來定義注解将用于什麼地方。@Retention用來定義該注解在哪一個級别可以用(在源代碼中 SOURCE、類檔案中 CLASS、運作時 RUNTIME)。  注解的元素看起來像接口的方法,唯一差別就是可以指定預設值。      沒有元素的注解稱為标記注解。

               4.注解的元素在使用時表現為 名 —— 值 對的形式,并需要置于注解聲明之後的括号内。

               5.Java目前隻内置了三種标準注解,以及四種元注解。元注解專職負責注解其他的注解。

                >.@Target :表示該注解可以用于什麼地方。可能的ElementType參數包括:CONSTRUCTOR:構造器的聲明、 FIELD:域聲明(包括enum執行個體)、LOCAL_VARIALBE:局部變量聲明、METHOD:方法聲明、PACKAGE:包聲明、PARAMETER:參數聲明、TYPE:類/接口/enum聲明

                >.@Retention :表示需要什麼級别儲存該注解資訊。 可選的RetentionPolicy參數包括: SOURCE、CLASS、RUNTIME。

                >.@Documented : 将此注解包含在Javadoc中

                >.Inherited :  允許子類繼承父類中的注解。

               6.使用注解的過程中,很重要的一個部分就是建立與使用注解處理器。

               7.注解元素可用的類型包括: 所有基本類型、String、Class、enum、Annotation、以上類型的數組。

               8.注解不支援繼承。

               9.單元測試是對類中的每個方法提供一個或多個測試的一種實踐,其目的是為了有規律地測試一個類的各個部分是否具備正确的行為。

##### 第二十一章:并發

               1.如果想要一個程式運作得更快,那麼可以将其斷開為多個片段,在單獨的處理器上運作每個片段。并發是用于多處理器程式設計的基本工具。并發通常是提高運作在單處理器上的程式的性能。

               2.如果程式中的某個任務因為該程式控制範圍之外的某些條件(通常是I/O)而導緻不能繼續執行,那麼我們就說這個任務或線程阻塞了。如果沒有并發,則整個程式都将停止下來,直至外部條件發生變化。

               3.實作并發最直接的方式是在作業系統級别使用程序。程序是運作在它自己的位址空間内的自包容程式。多任務作業系統可以通過周期性地将cpu從一個程序切換到另一個程序來實作同時運作多個(程序)程式。作業系統通常會将程序互相分隔開,是以它們不會彼此幹涉,這使得程序程式設計相對容易一些。

               4.Java的線程機制是搶占式的,這表示排程機制會周期性地中斷線程,将上下文切換到另一個線程,進而為每個線程都提供時間片,使得每個線程都會配置設定到數量合理的時間去驅動它的任務。

               5.并發程式設計使我們可以将程式劃分為多個分離的、獨立運作的任務。通過使用多線程機制,這些獨立任務(也被稱為子任務)中的每一個都将由執行線程來驅動。一個線程就是在程序中的一個單一的順序控制流,是以,單個程序可以擁有多個并發執行的任務,但是你的程式使得每個任務都好像有自己的CPU一樣,底層機制是切分CPU時間。

               6.線程可以驅動任務,是以需要一種描述任務的方式,這可以由Runnable接口來提供。要想定義任務,隻需實作Runnable接口并編寫run()方法,使得該任務可以執行指令。 任務的run方法通常總會有某種形式的循環,使得任務一直運作下去直到不再需要,是以要設定跳出循環的條件(有一種選擇是直接從run()傳回)。通常,run()被寫成無限循環的形式,這意味着,除非有某個條件使得run()終止,否則它将永遠運作下去。   在run()方法中對靜态方法Thread.yield()的調用是對線程排程器(Java線程機制的一部分,可以将CPU從一個線程轉移到另一個線程)的一種建議。 當從Runnable導出一個類時,它必須具有run()方法,但是這個方法并無特殊之處——它不會産生任何内在的線程能力,要實作線程行為,必須顯式地将一個任務附着到線程上。

               7.Thread構造器隻需要一個Runnable對象。調用Thread的start()方法為該線程執行必須的初始化操作,然後調用Runnable的run()方法,以便在這個新線程中啟動該任務。

               8.線程的優先級将該線程的重要性傳遞給了排程器。盡管CPU處理現有線程集的順序是不确定的,但是排程器将傾向于讓優先權高的線程先執行(優先權不會導緻死鎖)。優先級較低的線程僅僅是執行的頻率比較低。

               9.在使用并發時,将域設定為private是非常重要的,否則,sychronized關鍵字不能防止其他任務直接通路域,這樣就會産生沖突。

               10.原子操作是不能被線程排程機制中斷的操作,一旦操作開始,那麼它一定可以在可能發生的“上下文切換”之前(切換到其他線程)執行完畢。

               11.如果将一個域聲明為violatile的,那麼隻要對這個域産生了寫操作,那麼所有的讀操作就都可以看到這個修改。volatile域會立即被寫入到主存中。

               12.一個線程可以處于以下四種狀态之一:

                   >.建立(new):當線程被建立時,它隻會短暫的處于這種狀态。此時它已經配置設定了必需的系統資源,并執行了初始化。此刻線程已經有資格獲得CPU時間了,之後排程器将把這個線程轉變為可運作狀态或阻塞狀态。

                   >.就緒(Runnable):在這種狀态下,隻要排程器把時間片配置設定給線程,線程就可以運作。也就是說,在任意時刻,線程可以運作也可以不運作。隻要排程器能配置設定時間片給線程,它就可以運作。

                   >.阻塞(Blocked):線程能夠運作,但有某個條件阻止它的運作。當線程處于阻塞狀态時,排程器将忽略線程,不會配置設定給線程任何CPU時間。直到線程重新進入就緒狀态,它才有可能執行操作。

                   >.死亡(Dead):處于死亡或終止狀态的線程将不再是可排程的,并且再也不會得到CPU的時間,它的任務已結束,或不再是可運作的。任務死亡的通常方式是從run()方法傳回,但是任務的線程還可以被中斷。    

               13.一個線程進入阻塞狀态可能有以下原因:

                  >.通過調用sleep() 使任務進入休眠狀态,這種情況下,任務在指定的時間内不會運作。

                  >.通過調用wait()使線程挂起。直到線程得到了notify()或notifyAll()消息,線程才會進入就緒狀态。

                  >.任務在等待某個輸入/輸出完成。

                  >.任務試圖在某個對象上調用其同步控制方法,但是對象鎖不可用,因為另一個任務已經獲得了鎖。

               14.Thread類包含interrupt()方法,是以可以終止被阻塞的任務,這個方法将設定線程的中斷狀态。

               15.Sleep()、yield()  調用的時候,鎖并沒有釋放,wait()方法調用時,線程的執行被挂起,對象上的鎖被釋放。