1. 程式人生 > >【java】《java程式設計思想》 讀書筆記

【java】《java程式設計思想》 讀書筆記

之前主要用的C++的比較多,之前花了快2個月的實際認真系統全面的學習了以下java的基礎語法,《java程式設計思想》這本書翻譯水平確實不是很好,很多話讀著會比較拗口。推薦讀之前,先去網上搜索 “java程式設計思想勘誤”,當然,有時間,最好還是直接讀英文版。

網上書評價這個本書不太適合初學者,確實,站在計算機零基礎的人的角度上,堅持讀完確實是一個很大的問題,然後如果你有C++的基礎,或者計算機學習的經歷,相對來說確實會容易些。然而,不可否認,這確實是一本經典,在講述java語法的同時,把面向物件的思想解釋的淋漓盡致,它教給你的更是一種編寫程式的思維方式。因此,過段時間我打算再通讀這本書幾次。

接下去,放上我的讀書筆記,都是一些書中重點的歸納,看這個讀書筆記,同時回想知識,相當於又溫習了一遍這本書。

Thinking in java

ch2  一切都是物件

1.     java物件引用儲存於棧中,java物件都儲存於堆。(C++中物件的位置取決於上下文)。基本型別存棧中,都有正負號:boolean大小不確定,char 2bytes,long 8bytes。高精度數字類BigInteger和BigDecimal。

2.     類中new一個物件:bool初始化為false,基本型別0,0.0,物件為NULL,方法的區域性變數不初始化會出錯。

ch3ch4  操作符

1.     幾乎所有操作符只可操作基本型別,除了=,==,!=,能操作所有物件,String支援+,+=,用於連線字串,若表示式以字串開頭,後續所有運算元必須是字串型,若不是就強制轉換。注意表示式的提升現象。

2.     注意避免別名問題(操作的實際是引用)。並且==比較的也是引用,比較實際內容用equals(),基本型別的包裝類已被過載,其預設行為是比較引用。

3.     一元加號可以將較小資料型別運算元提升為int。

4.     >>>無符號右移位操作符,>>預設有符號右移。若右運算元超過範圍就取餘。若對byte或short值移位,可能得到不正確的結果:先被轉換為int,再右移,截斷,賦值給原來型別。

5.     java中的條件只能是boolean值,不同於C/C++。不可對boolean取~,且!只能對布林型操作,與或非(||)兩邊必須為布林值。

6.     float、double轉為整型總是執行截尾,可呼叫round()方法舍入。

7.     對陣列和容器可用foreach語法,range()方法指定範圍,效率稍微降低。

8.     continue和break可接標籤。

ch5 初始化與清理

1.     每個過載的方法必須有一個獨一無二的引數型別列表。

2.     除了char被提升為int(若有byte/short),其他若沒有響應的型別,則提升到最接近的。

3.     如果自己定義了一個構造器編譯器就不會建立預設的構造器。

4.     在構造器中呼叫構造器可用this(class name)呼叫:必須位於開頭,且只可呼叫一次。

5.     一旦垃圾回收動作發生,首先呼叫finalize()方法,並在下一次垃圾回收動作發生才真正回收物件所佔的記憶體。要知道呼叫了finalize()函式物件仍可能不被垃圾回收。

6.     System.gc()提醒希望虛擬機器進行一次垃圾回收,而垃圾回收動作與否是不確定的。
System.runFinalization()強制呼叫已失去引用物件的finalize()。

7.     垃圾回收的一種方法,自適應的垃圾回收技術:停止-複製和標記-清掃之中切換。從棧和靜態區出發,遍歷所有引用找到存活的物件。

8.     java可以直接在類中變數聲名處初始化變數。構造器初始化之前會執行自動初始化基本型別和物件引用。

9.     對靜態型別的初始化:只有在所對應的類被載入才啟動,先初始化當前類靜態變數(包括塊),然後是非靜態,然後是基本型別。
有個名為Dog的類,單個類建立物件的過程如下:

a)    找到Dog.class檔案並載入。

b)    靜態物件初始化,儲存區域清0,然後是new出來的物件並被置NULL,之後是基本型別置0。

c)     執行欄位定義處的初始化操作。

d)    執行構造器。

10.  變數定義的先後順序決定初始化順序。靜態方法中不能呼叫非靜態變數。靜態變數還可以在靜態塊static{}中初始化。非靜態示例也可以只是少了static關鍵字。

11.  可變引數可以由Object類+foreach語句實現,並且任何一個類都可以實現可變引數,當有多個過載方法時選擇最佳匹配。還要注意的是:帶可變引數的過載方法,所帶不可變引數形式也要一致。

12.  enum中可用ordinal()得到宣告的順序,並且會產生一個static values()方法按照宣告順序產生一個數組。

ch6  訪問許可權控制

1.     一個編譯單元(.java)的類名稱需與檔名稱相同,且在一個編譯單元內最多一個public。java可執行程式由一組.class檔案構成,java直譯器負責查詢,裝載,解釋,查詢方式:

a)    找出環境變數CLASSPATH(根目錄可含多個路徑,;分隔)

b)    將句點都換成/,將路徑與環境變數連線

2.     importstatic語句匯入使用類的靜態方法,簡化了操作。

3.     包訪問許可權對同個包中非private成員大家都可訪問,但對其他包的來說該成員是private。若當前2個檔案無包名,位於同個目錄下,就當做是預設包許可權。

4.     單例設計模式:private static Soup2 ps1 = new Soup2();
              public static Soup2access(){return ps1}
同時說明了不能因為在類中某個物件的引用是private,就認為其他物件無法擁有該物件的public引用。

5.     類的訪問許可權僅有包訪問許可權和public。

ch7  複用類

1.     組合:在新類中產生現有類的物件。通常用於想在新類中使用現有類功能而非介面。
繼承:用現有類的形式並在其中新增新程式碼。(是否需要向上轉型?)
代理:新類使用基類物件,不用繼承,在新類中重新封裝。

2.     print(類名)會呼叫類的toString()方法。

3.     每個類都有一個根基類Object。

4.     在繼承類後,若匯出類是無參構造器,java會在匯出類的構造器中自動呼叫基類的無參構造器。若匯出類構造器有參,需要在其構造器顯示呼叫基類構造器,如super(i)。

5.     若要自己手動定義清理函式,清理的順序要與構造的順序相反。比如清理動作放在finally塊(無論try任何執行結果,finally總會執行)。

6.     若基類含一個已被過載多次的方法名稱,那麼在匯出類中重新定義該方法並不會遮蔽基類中的任何方法,至少需要返回值,引數不同區分一個過載方法。

7.     @override註解防止你在不想過載時意外的過載。

8.     final關鍵字:

a)    final資料:必須在定義處或構造器中初始化。

                i.         編譯時常量,必須為基本資料型別。

              ii.         執行時初始化的值,而你不希望它被改變。對物件引用,final使引用恆定不變。如private final int[] a,其中a[i]還是可以改變的。

             iii.         finalstatic只佔據一段不能改變的儲存空間。即無論生成多少個物件,該成員值僅在第一次建立被初始化。編譯時常量和執行時常量都有可能。

b)    final方法:禁止覆蓋該方法。private方法不是基類介面的一部分,屬於隱藏在類中的程式碼,對其他類是不可見的。

c)     final類:不能被繼承。

ch8  多型

1.     多型:也稱作動態繫結。回想動態繫結的使用方法。增加了程式的可擴充套件性,並且將改變的事物與未變的事物分離開來。
向上轉型:把對某個物件的引用視為其基型別的引用的做法被稱作向上轉型。

2.     java中除了static和final方法(private方法屬於final方法)之外,其他所有的方法都是後期繫結。

3.     只有非private方法才可以被覆蓋,在匯出類中對基類中的private方法,最好採用不同的名字。

4.     只有非靜態方法呼叫才可以是多型的,不包括基本資料型別成員。靜態方法與類相關聯,而不是與某個物件。

5.     包含繼承的類初始化過程:

a)    父類靜態成員,靜態塊按照宣告順序初始化。

b)    子類靜態成員,靜態塊按照宣告順序初始化。

c)     父類基本型別,普通成員初始化,構造器執行。

d)    子類基本型別,普通成員初始化,構造器執行

6.     注意一種情況:如果在父類中方法中呼叫子類未初始化的成員會招致災難,(相當於上面第三步呼叫子類的成員)此時呼叫的成員為0還未被子類初始化。在構造器唯一能安全呼叫的是那些基類本身的final方法。構造器中應該儘量避免呼叫其他方法。

7.     協變返回型別:在匯出類的覆蓋方法處,可以返回基類被覆蓋方法返回型別的某種匯出型別。協變返回型別允許返回了更具體的型別而不一定是基類。

8.     更好的方式是首先選擇“組合”,尤其是不能十分確定應該使用哪一種方式時。繼承在編譯時就需要知道確切型別。我們可以在類中定義一個基類物件,定義一個函式改變引用的指向從而實現動態靈活性。

9.     繼承:is-a    is-like-a關係在匯出類包含了擴充套件介面

10.  子類物件賦給基類物件後,我們還可以進行向下轉型,在執行期間對型別進行檢查的行為稱作“執行時型別識別”RTTI,向下轉型失敗時,丟擲一個異常。

ch9  介面

1.     abstract關鍵字定義了抽象類和抽象方法,抽象類中可以包括已經定義的方法。抽象類的另一個作用是:阻止產生這個類的任何物件。抽象基類中宣告一個抽象方法接受基類引用,子類中定義該方法後,傳入子類引用不需要向下轉型。P172

2.     interface產生一個完全抽象的類,沒有提供任何具體實現,且介面中的域隱式的為final和static。與implements關鍵字配套使用。且介面中被定義的方法必須是public的。當介面類被當做形參時,無法區分它是一個普通類,抽象類還是介面。

3.     一般來說,只可以將extends用於單一類,但是可以引用多個基類介面。
例如:interface A extends B,C{}其中ABC均為介面

4.     策略設計模式:建立一個能根據所傳遞的引數物件的不同而具有不同行為的方法。P175最簡單就是利用多型,多個子類覆蓋一個方法。基類引數接受子類引用即可。

5.     對2個不同無關的類(比如一個介面和類),成員有相同的形式,且當無法修改你想要使用的類,可使用介面卡設計模式:接受你所擁有的介面,併產生你所需要的介面。P177

6.     沒有任何與介面相關的儲存,因此一個類可以實現多個介面,但是隻能extends一個。

7.     當想要建立物件時,所有的定義都首先必須存在。

8.     介面的優點:

a)    為能夠向上轉型為多個基型別。

b)    防止建立該類的物件。
介面常與向上轉型一起使用P180

9.     過載方法僅僅通過返回型別是無法區分的。如果在打算組合的不同介面中使用相同的方法名通常會造成程式碼可讀性的混亂,應該儘量避免。

10.  適配介面的含義:“可以用任何物件來呼叫我的方法,只要你的物件遵循我的介面”P182。因此得出一個重要結論:讓方法形參接受介面型別,是一種讓任何類都可以對該方法進行適配的方式。

11.  因為介面中的域是static的,初始化發生在第一次被載入時(被首次訪問時)。

12.  巢狀介面P185,注意類中的私有介面,無法得到他的例項,即使通過方法返回值也無法儲存,只能當做同個類其他方法的形參傳入。

13.  工廠方法:不是直接呼叫構造器,而是在工廠物件上呼叫建立方法,而該工廠物件將生產介面的某個實現的物件。實現了我們的程式碼與介面的分離。P188

14.  當必需時,更應該重構介面而不是匯出新增額外級別的間接性(建立介面和工廠)。恰當的原則應該是優先選擇類而不是介面


ch10  內部類

1.     類中的靜態方法可以有自己的區域性變數,呼叫一次靜態函式就銷燬,不可以直接使用類中的非靜態成員,必須例項化後才可用。比如main可以直接呼叫同個類中static成員。

2.     若想從外部類的非靜態方法之外的任意位置建立某內部類的物件,需明確指明內部類的型別:OuterClassName.InnerClassName,通常定義一個函式來獲得對內部類的引用。而且必須要先建立一個外部類物件。同個類的main中則不用。

3.     內部類擁有對其外圍類的所有元素的訪問權,包括私有變數。非靜態內部類是通過祕密地捕獲一個指向那個外圍類物件的引用做到的。

4.     通過在內部類的函式return 外類名.this顯式獲得對外部類的引用。

5.     .new用法:比如在同個類的main中,必須先建立一個外圍類,再建立一個內部類:(除了為建立巢狀類即靜態類)。注意在同個類的非靜態函式中,可以直接new Inner();

a)    Outerou = new Outer();

b)    Outer.Innerin = ou.new Inner();

6.     內部類可以return子型別或介面的實現,但是返回值為基類或介面型別,從而實現了向上轉型,隱藏了實現細節。

7.     外部類可以訪問內部類的private元素,但是需要通過外圍類的函式成員。

8.     在方法的作用域內創造一個完整的類稱為區域性內部類,此時的類在函式外就不可用了,並且不能有訪問說明符。當我們加上if時,在if外就不可用了,但此時不論if的條件真假,類都已經編譯過,只是為假時,不執行if內部的class之外的語句。

9.     建立一個繼承自Contents的匿名類的物件,並通過new表示式返回的引用被自動向上轉型為對Contents的引用:

a)    publicContents contents(final Stringdest){   //引用一個外部定義的要在匿名類中使用的物件需要final
  return new Contents(x){    //可傳入帶引數的構造器,基類要有對應構造器
         類成員; 
  };        //分號表示表示式的結束
}

10.  匿名類中產生構造器的效果就是通過例項初始化,即通過傳入的引數。

11.  匿名類結合工廠方法P200:
public static ServiceFactory  factory =
    new ServiceFactory(){
         public Service getService(return new Impletation1() );
    };

12.  普通的內部類不能有static資料、方法、巢狀類,因為內部類要持有外部類的指標,靜態方法和成員獨立於例項存在與之矛盾。但是巢狀類可以包含這些東西。

a)    當你想要建立某些公共程式碼,使得他們可以被某個介面的不同實現所共有,可以在內部類中實現其外圍介面。

b)    可以使用巢狀類存放包含main的程式碼,在釋出時可以刪除該巢狀類。外圍類並未攜帶任何巢狀類的程式碼。

13.  內部類最吸引人的原因:每個內部類能獨立的繼承自一個介面的實現,相當於實現了“多重繼承”,即內部類允許繼承多個非介面型別。同時獲得的特性:

a)    內部類可由多個例項,每個例項都有自己的狀態資訊。內部類就是一個獨立的實體。

b)    單個外圍類中,可讓多個內部類以不同方式實現同個介面或繼承同個類。

14.  內部類可以提供閉包的功能,通過定義一個函式return 內部類;返回介面型別實現。並且在外部類中通過傳入引用的不同,可以實現回撥,即執行時動態決定呼叫方法。

15.  內部類的另一個作用就是將不變的事物與變化的事物相互分離。比如內部類繼承一個抽象類,實現其中的抽象方法。類似於“多重繼承”。

16.  如果只是繼承一個內部類,構造器必須接受外圍類物件enclosingClassReference,並且呼叫enclosingClassReference.super();

17.  如果要覆蓋一個內部類的方法必須明確繼承這個內部類而不是繼承他的外圍類。且當呼叫這個方法時會先呼叫基類的方法,再呼叫被覆蓋方法。

18.  區域性內部類和匿名內部類的比較:

a)    我們需要一個已命名的構造器,或需要過載構造器使用前者。

b)    或者需要不止一個該內部類的物件,要要使用區域性內部類。

19.  內部類也包含一個.class檔案儲存他們的資訊,命名方式:外圍類名$內部類名,若是匿名類編譯器會簡單地產生一個數字作為識別符號。

ch11  持有物件

1.     使用泛型可以在編譯期防止將錯誤型別的物件放置到容器中。向上轉型也可作用於泛型。

2.     Arrays.asList()方法接受一個數組或是一個用逗號分隔的元素列表並轉化為一個List。當多層繼承時,必須要Arrays.<Snow>asList();來顯式型別引數說明,下面一種方法則不用。
Collection.addAll(collection,moreInts);為首選方式。

3.     List:一種可修改大小的序列

a)    ArraysList:擅長隨機訪問元素,插入和移除元素較慢。

b)    LinkedList:擅長插入和刪除元素,隨機訪問較上者慢。但是特性集較上者大。可用於實現棧、佇列、雙端佇列。包括的方法見JDK文件。

                i.         其中poll()移除頭並返回,列表為空時返回NULL,其他則丟擲異常。

              ii.         offer()表示新增到末尾。

             iii.         在實現棧中,當名稱與JDK中的stack衝突時,通過顯示的匯入來控制對首先Stack實現的選擇。

c)     幾個常用方法:都受equals()函式影響。

                i.         contains(),containsAll()

              ii.         remove(),removeAll()

             iii.         subList()得到一個子集

             iv.         set(),在第一個引數位置處,用第二個引數替換整個位置的元素

               v.         包含一個過載的addAll()方法,可以用於插入中間

             vi.         toArray()方法,將任意的Collection轉換為一個數組

d)    LinkedList派生於Queue,Collection包含了一個可用的Queue。

                i.         peek和element不移除的情況下返回隊頭。佇列為空時peek返回NULL,後者丟擲異常。

e)    PriorityQueue宣告下一個彈出元素是最需要的元素,預設允許重複,升序列印,可以在呼叫其構造器時第二個引數,使用Collection.reverseOrder()產生反序的Comparator

4.     Set:主要用於查詢。插入操作只可插入不存在的元素。Set與Collection具有完全一樣的介面,沒有新增任何額外的功能。

a)    HashSet:基於雜湊函式,獲取元素最快。

b)    TreeSet:基於紅黑樹,按升序排列。

c)     LinkedHashSet:使用了雜湊加連結串列,按照被新增的順序排列。

d)    常用的操作:

                i.         set1.contains(“H”);是否包含H元素。同樣有containsAll()方法。還有removeAll()方法。

              ii.         可以使用TextFile開啟一個檔案並讀入一個Set中。預設字典序,若要改為字典序向TreeSet構造器傳入String.CASE_INSENTIVE_ORDER

5.     Map:

a)    HashMap:速度最快。

                i.         典型用法:Integer freq=m.get(r);
      m.put(r, freq == null ? 1 :freq+1);

              ii.         常用方法:containsKey(xx),containsValue(xx)。keySet(),values() 返回此對映中包含的鍵的 Set 檢視與Collection檢視。

b)    TreeMap:升序儲存鍵。

c)     LinkedHashMap:按照插入順序儲存鍵。

6.     迭代器的作用:不重寫程式碼就可以應用於不同型別的容器,將遍歷序列的操作與序列底層的結構分離,即統一了對容器的訪問方式。

a)    next()獲得序列中的下一個元素

b)    hasNext()檢查序列中是否還有元素

c)     remove()將迭代器新近返回的元素刪除,即remove()前必須先呼叫next().

d)    ListIterator屬於Itetator的子型別,但可以雙向移動。可以看做初始
it.nextindex = 0,it.preindex = -1,執行一次next()兩者都加1,previous則兩者都減1。我們得到的next()元素就是序號nextindex處的元素。我們還可以ListIterator<Pet> it = pets.listIterator(3)直接操作指定序列處的元素。

7.     Collection和Iterator        P240

a)    接受引數為Collection:當要實現一個不是Collection的外部類時,可通過繼承AbstractCollection並實現其中的iterator()和size()方法。

b)    接受引數為迭代器:繼承並且提供建立迭代器的能力實現起來容易的多。只需要在新類定義一個Iterator<xx>()內部類,並在其中實現next,hasNext,remove方法。

8.     所有Collection物件都能與foreach語句一同工作。其中原理是因為實現了Iterable的介面包括next,hasNext和remove三個方法。

9.     迭代器形參不接受陣列型別,但可以接受Arrays.asList()的返回值。

10.  假如想要新增多種在foreach語句中使用這個類的方法,可採用介面卡模式。P243

11.  Arrays.asList()產生的List物件會使用底層陣列作為其物理實現,如果你執行的操作會修改這個list,而你並不想其被修改,你就應該在另一個容器中建立一個副本:
List<String> shuffled = new ArrayList<String>( Arrays.asList(words));

12.  容器不能持有基本型別,但是自動包裝機制會仔細地執行基本型別到容器中所有的包裝型別之間的雙向轉換。

13.  除了TreeSet之外的所有Set都擁有與Collection完全一樣的介面。Map和Collection之間的唯一重疊就是Map可以使用entrySet和values方法來產生Collection。Queue介面都是獨立的,因此在建立具有Queue功能的實現時,不需要Collection方法。

ch12  通過異常處理錯誤

1.     異常發生時:

a)    new一個異常物件

b)    彈出對異常物件的引用

c)     進入異常處理程式

2.     終止模型與恢復模型。

3.     建立自定義異常:class My extends Exception(){ super(msg) }本例還呼叫了基類的帶引數構造器。

4.     throws宣告一個異常,表明此方法將產生異常,假如方法的程式碼裡產生了異常卻沒有處理,編譯器將提醒你宣告或者處理。不宣告表示不會產生異常。
throw表明丟擲一個異常。

5.     e.printStackTrace();輸出資訊到標準錯誤:從方法呼叫處到異常丟擲處的方法呼叫序列。

6.     java.util.logging提供了記錄日誌的功能:我們通常需要捕獲和記錄其他人編寫的異常,因此通常在catch中呼叫生成日誌方法。P254

7.     可以通過覆蓋Throwable.getMessage()方法得到更詳細的資訊。

8.     通過catch(Exception e)可以捕獲所有異常,最好把它放在處理程式列表的末尾。Exception是所有的基類,它的基類是Throwable類。Throwable類分為Error和Exception兩種型別。

9.     e.getStackTrace()得到一個棧軌跡的陣列,元素0是棧頂,就是異常被丟擲處。

10.  重拋異常會把異常拋給上一級環境中的異常處理程式,同一個try塊的後續catch子句將被忽略。P258  

a)    throw(Exception)e.fillInStackTrace( );列印新的棧軌跡,否則列印原來的棧軌跡(包括重拋異常經過的方法)。捕獲一種異常後丟擲一種異常的做法與此情形類似。

b)    構成一個異常鏈:ExA a = new ExA( );   //建立一個異常物件
               a.initCause(newNullPointerException( ));
               throw a;
如果在下面的catch塊捕捉ExA異常並列印棧軌跡,會有NullPointer異常的資訊一同打印出來。

11.  只能在程式碼中忽略RuntimeException型別的異常(未處理的話,main結束自動列印棧軌跡),其他型別異常處理都是由編譯器強制實施的。

12.  當要把除記憶體之外的資源恢復到他們的初始狀態時,就要用到finally子句,比如檔案描述符。

a)    可以保證在方法有多個點返回的情況下清理工作仍然執行。

b)    但有可能會因為覆蓋而導致異常的丟失。

13.  異常限制:覆蓋方法時,只能丟擲在基類方法的異常說明裡列出的那些異常。

a)    基類丟擲異常,子類覆蓋後方法後要麼不拋,要麼丟擲與基類一樣的基礎,要麼丟擲基類異常的派生類。

b)    介面f()丟擲異常A,其中的f()定義來自基類並丟擲異常C,子類繼承基類並實現該介面後覆蓋f()必須丟擲與基類相同的異常C。

c)     條件同上,基類中沒有同名方法,可以實現該介面丟擲異常A。

14.  如果用法恰當的話,直接向上層丟擲異常能簡化程式設計。對於在構造階段可能丟擲異常,並且要求清理的類,最安全的使用方法就是使用巢狀的try子句。通常要求應該建立不能失敗的構造器。

15.  對一個基類異常來說,其異常的派生類也可以匹配基類異常。

16.  C屬於弱型別語言,C++和java為強型別靜態語言,編譯時就做型別檢查。反射和泛型就是用來補償靜態型別檢查所帶來的過多限制。

17.  可以把被檢查的異常包裝進RuntimeException中,相當於“遮蔽”。


ch13  字串

1.     String類的方法實際傳遞的只是原物件引用的一個拷貝。只有當方法執行,其區域性引用才存在,返回的引用也指向了新的物件,原本引用還在原地。

2.     反彙編java程式碼:javap -c  xxxx   其中-c表生成JVM位元組碼
對String的+的過載,編譯器使用了效率更高的StringBuilder類的append()方法。當你為一個類編寫toString()方法時,如果你要在其中編寫迴圈,最好明確的建立一個StringBuilder物件,這樣可以避免產生過多的StringBuilder。

3.     在toString中列印物件的記憶體地址:super.toString()。直接呼叫this.toString()會造成遞迴呼叫。

4.     注意String的length()和陣列上的length

5.     System.out.format用法與C中printf()用法一致。

6.     格式化功能可以由java.util.Formatter類處理,構造其物件時傳入一個引數System.out或者System.error。之後直接呼叫它的format函式即可。

a)    f.format(“%-15s%5s %10.2f\n”, “Tax“, “”, total*0.06);其中-表示左對齊,預設右對齊。

b)    對於format中的轉換,char不可轉整數,浮點。對於boolean基本物件或其包裝類,其轉換結果永遠是true或false。但對其他型別,只要不為null,永遠都是true。

7.     String.format與前一個用法一樣,但返回一個String物件。

8.     開啟、讀入二進位制檔案:net.mindview.util.BinaryFile。
System.out.println(format(BinaryFile.read(“Hex.class”)))在format中新建一個StringBuilder物件result儲存結果,逐位元組遍歷,存入其中,最後returnresult.toString();

9.     java正則表示式中表示一個普通的反斜槓:////。第一個斜槓轉義了第二個,第三個斜槓轉義了第四個。

10.  java對反斜線/的處理不同於其他語言,使用時候需要轉義,但是* .之類的特殊字元不用轉義就保持特殊含義。

11.  -?\\d+ 表示可能有一個或零個負號,後面跟著一位或多位數字

12.  (-|\\+)?  表示字串其實字元可能是一個-或者+,或2個都沒有。後一個\轉義+,前一個\轉義\

13.   Java 匹配點無特殊含義的(.) 或  {  [  (  ?  $  ^ 和 * 前面加雙斜框。
java層------正則表示式------實際含義     常採用從實際含義逆推的方法。

14.  最簡單應用正則的方法就是用String類的:

a)    mathes(“正則”)。前一個\對後一個\轉義。

b)    split()方法,接受一個正則引數,在原始字串中與正則匹配的部分在最終結果中都不見了。

c)     replaceFirst()和replaceAll()

15.  量詞:P299

a)    貪婪型:只要字串中存在匹配項就一直匹配下去。可能得到多個匹配。

                i.         X?一個或0個     X*零個或多個       X+一個或多個

b)    勉強型:最小匹配的。

c)     佔有型:會產生很多狀態以便在失敗時回溯。

16.  Patternp = Pattern.compile(正則表示式);
Mathcher m = p.mathcher(目標字串);

17.  "(?i)((^[aeiou])|(\\s+[aeiou]))\\w+[aeiou]\\b“  \s空格 \w詞字元

18.  Matcher類中lookingAt()有正則表示式與輸入最開始的第一部分匹配就成功
             matches()在正則表示式與輸入整個部分匹配就成功

19.  Pattern.compile()可加第二個引數 | 分隔,或者直接在正在表示式開頭括號內指出()

a)    Pattern.CASE_INSENSITIVE(?i)忽略大小寫

b)    MULTILINE(?m)  開啟多行模式

20.  print(Arrays.toString(Pattern.compile(“!!“).split(input)));

21.  Matcher物件的reset()方法可將現有的Matcher應用於下一個新的字元序列。

22.  \\b[Ssct]\\w+  搜尋以Ssct開頭的單詞

23.  \\w+\\.{1}\\w+搜尋詞字元開頭類似於: xx.xx 形式的字串(查詢.java檔案)
^$匹配空行

24.  publicstatic BufferedReader input = new BufferedReader(
  new StringReader(“This is a test”) )    //假設屬於類SimpleRead

a)    把String類轉換為流並作為BufferedReader構造器的引數。

b)    Scannerstdin = new Scanner(SimpleRead.input);接下去可以直接使用stdin.nextInt()等方法,next()方法得到下一個String。

c)     Scanner類有一個假設,輸入結束會跑出IOException,所以Scanner類把IOException吞掉。我們仍可以通過ioException找到最近發生的異常。

d)    預設情況Scanner類使用空格作為定界符,使用useDelimiter()可以用正則表示式指定自己所需的定界符。delimiter()方法返回正作為定界符使用的Pattern物件。

e)    可以配合用正則表示式掃描字串P311重要

ch14  型別資訊

1.     執行時識別型別資訊實現包括兩種方式

a)    RTTI:編譯時開啟檢查.class檔案。主要包括三種形式:

                i.         在.class物件上

              ii.         instanceof

             iii.         強制型別轉換(父子類的轉換,有可能造成編譯時錯誤)

b)    反射:執行時開啟檢查.class檔案

2.     類載入的過程:首先類載入器檢查這個類的Class物件是否已經載入,未載入則預設載入器根據類名查詢.class檔案,在這個類的位元組碼被載入時,會先驗證保證完整和安全。

3.     Class.forName(“Gum”);執行時返回Class物件的一個引用並指向Gum。

Fancytoy.class使用類字面常量,編譯時檢查,更加高效。步驟:載入,連結(驗證位元組碼),初始化。

4.     Class的幾個常用方法:

a)    getSimpleName,getName,isInterface,getSuperclass,getInterfaces

b)    getClass根據物件獲取Class引用

c)     使用newInstance建立的類必須帶預設構造器

d)    getDeclaredFields得到宣告的資料,可儲存在Object陣列中。getDeclaredMethod得到宣告的方法。

e)    getMethods得到可公共訪問的方法和介面,包括繼承過來的介面和方法

5.     我們可以通過getSuperclass和instansof來區別基本型別和物件。

6.     對static final的訪問可以在初始化類之前,但是也可能作為執行時常量,此時訪問它就必須先初始化類。而對static非final的變數訪問則是在連結和初始化之後。

7.     使用泛化的Class在編譯期就可以檢查出錯誤而不是執行時才發現。

a)    放鬆限制:Class<? extendsNumber>  a = int.class;

b)    Class<?super Interger> b = a.getsuperclass(); b的型別不能Number,並且當呼叫newInstance()的時候返回型別也不能是具體型別,而得是Object。

8.     privatestatic List<Class<? extends Pet>> types =
                         newArrayList<Class<? extends Pet>>();

a)    第一種新增方法,必須使用轉型:types.add(
                          (Class<?extends Pet>) Class.forname(name) );
要計數的話就繼承一個HashMap<String, Integer>定義count方法,instanceof滿足就調對應count。此方法會產生很多的判斷語句。

b)    使用類字面常量:public static final List<Class<? extends Pet>>  alltypes =
Collections.unmodifiableList(Array.asList(Cat.class, …) );之後再用sublist取子集。

c)     改進(count引數pet):for(Map.Entry<Class<?extends  Pet>, Integer> pair
                         : entrySet())
                       if(pair.getKey().isInstance(pet)) //動態的instanceof
                                 put(pair.getKey(), pair.getValue() + 1);

d)    basetype.isAssignableFrom(type)  //type為basetype本身或其子類才返回真。

9.     工廠方法設計模式:將物件的建立工作交給類自己去完成。相當於建立一個內部類,然後其中定義一個方法作為建立類的方式。

10.  涉及比較時,instanceof保持了類的概念包括本身和子類,而==則比較實際的Class物件,沒考慮繼承。

11.  代理的目的是為了進行額外的操作,並將額外的操作從實際物件中分離到不同的地方。 使用的場合是除錯和遠端方法呼叫。

使用代理的步驟如下:

a)    建立一個實現介面InvocationHandler的類,它必須實現invoke()方法。

b)    建立被代理的類以及介面。

c)     通過Proxy的靜態方法newProxyInstance(ClassLoaderloader, Class<?>[]         interfaces,InvocationHandler h)建立一個代理。

d)    通過代理呼叫方法。(在一個新類中)

12.  使用代理要注意的地方:

a)    通過invoke()方法的第一個引數就是被代理物件,然後通過該物件呼叫對應的方法。

b)    額外的處理都放在invoke中。

13.  空物件的意義是可以接受傳遞給它的所代表物件的訊息,但是將返回表示未實際並不存在“真實”物件的值。通過這種方式可以假設所有物件都是有效的。

14.  P344匿名類作函式引數時直接實現介面,逗號作為單個實現的結束。

15.  反射可以到達並呼叫所有的方法,包括private,沒有辦法可以阻止。P349   

a)    通常的步驟:

                i.         TobeVistedpf = new TobeVisted();

              ii.         Fieldf = pf.getClass().getDeclaredField(“i”);       //訪問i變數

             iii.         f.setAccessible(true);

             iv.         之後即可通過f.getInt(pf)等方法訪問和修改pf的資料成員了

b)    對final變數在遭遇反射修改時是安全的,系統在不丟擲異常的情況下接受任何修改,但是實際上不會發生修改。

16.  面向物件的目的是讓我們在凡可以使用的地方都使用多型機制,只在必須的時候用RTTI。比如基類被別人控制,此時就可用RTTI,我們繼承它,然後新增需要的方法。且RTTI有時能解決效率問題。應當儘量編寫能夠進行靜態檢查的程式碼。

ch15 泛型

1.     多型算一種泛化機制。泛型的意義就是使程式碼能應用於某種不具體的型別,而不是具體的介面或類。通常容器只用來儲存一種型別的物件。泛型的主要目的之一就是指定容器持有的型別物件,而且由編譯器保證型別的正確性。

2.     元組tuple是將一組物件(型別可不同)直接打包儲存於其中的一個單一物件。public class TwoTuple<A,B>{…}            之後就可以當做返回值返回了。

3.     不用LinkedList實現自己的鏈式儲存棧:內部定義一個節點類。push方法將設定當前節點和下一個結點為原來的top,並更新top。pop要使用一個末端哨兵判斷棧何時為空P357。

4.     介面本身的許可權同其他類,其中的變數都是public static final,方法都是public abstract。

5.     要想使某個其他類可以使用foreach語法,接讓這個類先實現Iterable<Coffee>介面:之後:public Iterator<Coffee> iterator() 然後在return語句中實現匿名類,在其中實現hasNext,next,remove方法即可。

6.     基本型別無法作為泛型的型別引數,但是有自動打包和拆包機制,方便了轉換。

7.     應當儘量使用泛型方法,具體引數型別推斷由編譯器負責。我們同樣可編寫一個工具類簡化我們的型別推斷工作,但是注意這裡的型別推斷只對賦值操作有效,做函式引數不行如New.map()。使用型別推斷+static可以使使用成為更通用的工具類庫。

8.     可變引數列表可以和泛型很好的共存。

9.     為創造一個通用的生成器,首先需要繼承generator<T>,根據傳入的.class物件newInstance一個新物件。要求這個類必須為public且包含預設的構造器。

10.  泛型同樣可應用於內部類和匿名內部類。

11.  可以很容易的用容器的泛型類建立複雜模型。P372 shelf->Aisle->Store

12.  在泛型程式碼內部,無法獲得任何與泛型型別有關的資訊。因此比較兩個不同泛型型別的.class結果會使true

13.  java編譯器中無法像C++一樣保證能夠在無法呼叫obj上的某個f()時出錯。我們必須給定泛型類的邊界,告知編譯器只能遵循這個類的邊界。歷史原因:保證遷移相容性,即保證現有的程式碼和類檔案仍舊合法。

14.  泛型型別只在靜態型別檢查期間出現,此後程式中所有泛型型別都將被擦除,被替換為它們的非泛型上界。

15.  在泛型中的所有動作都發送在邊界處:對傳遞進來的值就行額外的編譯器檢查,並插入對傳遞出去的值的轉型。

16.  通常擦除的補償採用型別標籤:顯式地傳遞Class物件,就可以使用isInstance了。

17.  建議使用顯式的工廠建立型別例項:並將限制其型別,使得只能接受實現了這個工廠的類。編譯時檢查注意與前面執行時的newInstance對比。P382

18.  還可以用模板方法設計模式建立型別例項,定義抽象類,再定義一個實現類實現其中的create()方法得到型別新例項。

19.  利用newInstance()的引數版本建立某個類的物件:
Class.forName(typename).getConstructor(args[0].getClass(),args[1].getClass()).newInstance(args[0],args[1]);

20.  泛型陣列:通常可用ArrayList代替,不僅可獲得陣列的行為,還有編譯時檢查。其他:

a)    gia= (Generic<Integer>[])new Generic[SIZE];還可建立一個被擦除的陣列然後轉型。

b)    傳入型別標籤T,之後再轉型:array = (T[])Array.newInstance(type,size)。注意若不傳入型別標籤,T會被擦除到Object

21.  classSolid<T extends Dimension & HasColor & Weight>{} extends後類必須先寫出來,然後才是介面。

a)    可以在繼承的每個層次上新增邊界限制,從而減少重複書寫,比如:
class Solid2<T extends Dimension & Hascolor & Weight>
extends ColoredDimension2<T>{}

22.  List<Fruit>flist = new ArrayList<Apple>();編譯時錯誤,不能把一個涉及Apple的泛型賦值給一個涉及Fruit的泛型。
但是如果我們List<? entendsFruit> flist = new ArrayList<Apple>();就可以成功。但是一旦執行這種型別的向上轉型,你將丟失掉向其中傳遞任何物件的能力,包括Obj

23.  陣列的協變性:比如開始num[0] =  new Integer(0);之後num[0] = new Double(3.4)就會執行時出錯。此協變性對使用了萬用字元?的List不成立。

24.  原則:

a)    從資料型別中讀: ? extends
假如有List<? extends Fruit> flist = newArrayList<Apple>();只可以呼叫contains和indexOf這些方法,而不能add()。因為?表示任何繼承自Fruit的類(上界),你不能保證這個類一定是Apple。

b)    寫入資料結構:  ?super Apple
假如有List<? super Apple>apples = new ArrayList<Fruit>();apples.add(x),x可以是Apple(下界)的子類或本身。因為?表示任何以Apple作為基類的類,肯定包含Apple的子類或本身的基類Apple的公共部分。

c)     都需要:不要用萬用字元。

25.  無界萬用字元:P397例子

a)    List表示持有任何Object型別的原聲List,List<?>表示具有某種特定型別的非原聲List,只是不知道那種型別是什麼。

b)    還有一種用法:static Map<String, ?>map3 確定一個引數,另一個是不確定的某種型別。

c)     不能往List<?> list新增任意物件,除了null

26.  捕獲轉換隻有在這樣的情況下才能工作:在方法內部你需要使用確切的型別。此時需要特別使用<?>

27.  問題

a)    任何基本型別都不能做型別引數,且自動包裝機制不能應用於陣列。

b)    一個類不能實現同一個泛型介面的兩種變體,因為擦除。加入類B<a>實現介面A,類C不能繼承B,再實現介面A<b>。

c)     <T>預設擦除到Object

d)    有時需要List<Widget> lw2= List.class.cast(in.readObject());通過泛型類轉型,而不能轉型到實際型別。

28.  自限定的型別:價值在於可以產生協變引數型別。

a)    迴圈泛型:class Subtype extendsBasicHolder<Subtype>{} BasicHolder中定義了接受T型別的set和返回T的get方法。現在,在Subtype中set和get的引數只能是Subtype了。

b)    自限定:class SelfBounded<Textends SelfBounded<T>>{}
要求繼承關係中使用SelfBounded時必須按照:
class A extends SelfBounded<A>{}

c)     如果不使用自限定,將過載引數型別列表。使用了自限定,只能獲得方法的一個版本,他接受確切的匯出引數型別(不是基類)。

29.  Collections類包含解決型別檢查的靜態方法checkedMap(),checkedList()…

30.  catch語句不能捕獲泛型型別的異常,但是型別引數可能會在一個方法的throws子句中用到。比如:
interface Processor<T, E extends Exception>{
void process(List<T> resultCollector) throws E;}

31.  java中混型實現:

a)    使用介面:A和B是2個功能類實現介面a和b,在類C中要產生混型的效果:讓C實現a,b,在類C中繼承一個基類base並建立A和B的例項向上轉型,然後定義ab介面中方法即可。

b)    裝飾器模式:Basic類先包裝一層Decorator裝飾器類,之後將其他類包裝在這個可裝飾物件的四周,來將功能分層。主要在主類中採用組合方式即可實現混型效果。

c)     動態代理:代理可以被轉型為被代理物件。P416

32.  java對缺乏潛在型別機制的補償:

a)    反射,型別檢查在執行時:Class<?> spkr = speaker.getClass();
                       Method speak =sprk.getMethod(“speak“);
                      speak.invoke(speaker);     //執行方法

                i.         將一個方法應用於序列,可以如此宣告這個方法:
public static <T, S extends Iterable<? extends T>>
void apply(S seq, Method f, Object…args){
for(T t : seq)   f.invoke(t, args);} 這樣該方法可以接受任何實現了iterable介面的事物。

b)    介面卡模擬:定義一個介面包含同名方法,我們為每一個需要適配某方法的類建立一個介面卡類實現該介面。P424

33.  策略設計模式:將“變化的事物”完全隔離到一個函式物件中。P427體會思想

ch16  陣列

1.     陣列和容器的主要區別:效率,型別和儲存基本型別的能力。但是缺點是大小固定。

2.     物件陣列儲存的是引用,基本型別陣列儲存基本型別的值。陣列中length表示陣列長度而不是陣列實際儲存的元素個數。

3.     初始化

a)    聚集初始化:
Bery[] d = { new Bery(), new Bery(), new Bery() };

b)    動態初始化
a = new Bery[]{ new Bery(), new Bery(), new Bery() };可以直接做函式形參。

4.     建立三維陣列:int[][][] a = new int[7][][];
 第一層for:a[i] = new int[5][];  確定了一維,還要建立二維(共三維)
 第二層for:a[i][j] = new int[5]; 確定了二維,還要建立一維

5.     建立二維陣列:int[][] a = new int[7][];
第一層for:a[i] = new int[5];
第二層for:a[i][j] = i*j;

6.     當我們用基本型別去初始化對應的包裝型別時,自動包裝機制就會起作用。

7.     不能例項化具有引數化型別的陣列,通常宣告同類型非引數化型別再轉型為引數化。

8.     Object陣列能存放除了基本型別之外的任何物件。

9.     Arrays.fill()只能用同一個值或引用填充某些位置。

10.  策略設計模式的一個例項:Generator生成器。我們可以通過選擇Generator的型別來建立任何型別的資料。P443,P445展示了一組RandomGenerator。

11.  boolean[]result ,Boolean[] in。 自動拆箱機制:result[0] = in[0];

12.  注意:自動包裝機制不能應用於陣列://ia = Ia

13.  複製陣列:System.arraycopy(),如果複製物件陣列,只複製了物件的引用,不是複製了物件的拷貝。這被稱為淺複製。

14.  陣列相等的條件:個數相同,對應位置的元素也相等。equeals,deepequals。

15.  想將某個類的陣列應用於sort:

a)    實現Comparable<CompType>介面,並實現compareTo()函式,相等返回0

b)    實現comparator<CompType>介面,並實現compare函式,這樣可以在sort的第二個引數使用這個類了。

16.  java標準庫針對基本型別採用快速排序,針對物件採用穩定歸併排序。

17.  若對未排序的陣列使用Arrays.binarySearch()將產生不可預料的結果。找到目標,返回值大於0,負值a表示應該插入的位置-(a+1)

18.  對物件進行二分查詢,要在該物件實現Comparator並作為binarySearch第三個引數。

19.  泛型可以產生型別安全的容器,應當優先使用容器,除非效能稱為問題。

ch17  容器深入研究

1.     Collection.fill同Arrays中功能一樣,但只對List物件有效。還有Collection.nCopies()

2.     P461的CollectionData是介面卡設計模式的一個例項,它將Generator適配到CollecitonData的構造器。

3.     P462介紹了一種Map生成器。類似於CollectionData接受Genarator型別或迭代器型別引數,用以產生map

4.     享元設計模式:在普通的解決方案需要過多物件,或產生普通物件太佔用空間時使用。

5.     print一個map的原理就是,先通過entrySet()獲取Set檢視,再通過返回物件的迭代器取出訪問。P469簡單的使用享元示例。

6.     Collection的常用方法:addAll,toArray等P470

7.     如果一個操作是可選的,未獲支援的,實現後執行時會出現UnsupportedOperation-Exception異常。常見例子是由固定尺寸的資料結構支援的容器。任何改變其尺寸的方法都會產生異常,比如retailAll,add,addAll等等

8.     HashSet,首選,存入其中的元素必須定義hashCode()
TreeSet表示保持大小次序的Set,紅黑樹實現,必須實現Comparable介面
LinkedHshSet保持插入次序的Set,必須定義hashCode()

9.     所有儲存在Set、map中的類都必須具有equals方法。預設的hashCode是合法的(來自Object),即便他是不正確的。

10.  SortedSet是一個介面,可以保證元素處於排序狀態。通過向上轉型可以使用包括first,last,subSet等方法。

11.  填充到優先順序佇列的物件必須要實現Comparable介面並定義compareTo方法

12.  關聯陣列及其簡單的一個時間就是利用二維Object型別陣列

13.  要在Map中使用物件,要定義hashCode和equals方法(接受object型別)。instanceof會檢測物件是否為null

14.  map中查詢過程:計算雜湊碼,用雜湊碼查詢陣列。

15.  java的雜湊函式以前會使用質數,目前都使用2的整數次方。主要是消除除法和求餘的開銷,用掩碼代替。

16.  不應該使hashCode依賴於具有唯一性的物件資訊,尤其是this的值。加入多個String都包含相同的字串序列,則這些String物件都對映到同一塊區域。

17.  產生hashCode的一種方法:int result = 17;  result = 37*result+s.hashCode()
result = 37*result + id;     returnresult;     對每個有意義的域計算一個雜湊碼。

18.  P502:tests.add(newTest<List<Integer>>)(“add”){
         int test(){}
       };

19.  HashSet的效能基本比TreeSet好,但是用後者迭代較快。

20.  HashMap的效能因子:負載因子=尺寸(當前儲存的項數)/容量(表中桶位數)
負載輕的表插入和查詢比較理想,但是會減慢迭代器遍歷的過程。HashMap和HashSet允許你指定負載因子,當負載因子達到這個數時,自動增加容量。預設0.75。

21.  Collection.unmodifiableCollecton表示只讀,不可修改的。
Collection.synchronizedCollection同步控制

a)    快速報錯,比如在宣告迭代器之後add會產生異常。正確做法是新增完後再宣告。

22.  WeakHashMap用來儲存WeakReference:當程式需要那個“值”的時候,便在對映中查詢現有物件,然後再使用,而不是重新建立。並且他允許垃圾回收期自動清理鍵和值,只要當鍵值沒有被其他地方使用時。

ch19  Java I/O系統

1.     newFlile(“.”).list();  返回一個字串陣列,這些字串指定此抽象路徑名錶示的目錄中的檔案和目錄。

a)    還有一個接受引數型別為介面FilenameFilter的版本,我們必須在其實現中實現accept函式以便實現過濾器功能(通過正則),此後返回的就是過濾後的目錄了。

b)    最簡單的實現,就是定義一個作為list()引數的匿名內部類。體現了匿名內部類怎樣通過建立特定的一次性的類來解決問題。

2.     P528提供了local產生有本地目錄中的檔案構成的File物件陣列,walk產生給定目錄下整個目錄樹包含的目錄和檔案構成的List。

3.     File物件還可以建立新的目錄或不存在的整個目錄路徑,檢視檔案特性,刪除檔案等。

4.     面向位元組的I/O類:處理位元組和二進位制物件:InputStream和OutputStream。
面向字元:處理字元和字串:Reader和Writer

5.     I/O流的典型使用方式:

a)    緩衝輸入檔案:BufferedReader in = new BufferedReader(new FileReader(name));  String s = in.readline();  finally也要close

b)    從記憶體輸入:StringReader in = new StringReader(String s); 

c)     格式化的記憶體輸入:DataInputStream in = newDataInputStream(
                                      newBufferdInputStream(
                        new FileInputStream(“xx.java”)));
in.readByte()一次一位元組地讀取字元。available()檢視還有多少可供讀取字字元。

d)    基本的檔案輸出:PrintWriter out = new PrintWriter(
                         newBufferedWriter(new FileWriter(“xx.out”)));
簡化書寫可以直接PrintWriter out = new PrintWriter(“xx.out”);寫結束要close(),不然緩衝區內容不會被重新整理清空,造成不完整的情況。

e)    格式化的寫入:DataOutputStream類,用法類似輸入out.WriteDouble等

f)      讀寫隨機訪問檔案:RandomAccessFile相當於組合了c和e

6.     讀取二進位制檔案可以用BufferedInputStream類的read方法讀入byte陣列。

7.     對System.in必須包裝才可使用:比如BufferedReader stdin =new BufferedReader(new InputStreamReader(System.in));之後可以stdin.readline();

8.     標準I/O重定向:setIn(InputStream) setOut(PrintStream) setErr(PrintStream)

9.     Processprocess = new ProcessBuilder(command.split(“ ”).start();以指定程式和命令列引數啟動一個程序

10.  新I/O使用了nio修改了FileInputStream,FileOutputStream,RandomAccessFile類,通過對每一種上述類的物件getChannel()即可得到通道FileChannel類物件。可通過此物件wrtie或read,唯一與之互動的緩衝器是ByteBuffer,以原始的位元組形式或基本型別輸出和讀取資料(無法對物件作用)。

a)    注意:在read完之後,要flip()反轉此緩衝區。通常用於讀取已寫入資料。

b)    transferTo()和transeferFrom()允許兩個通道向連線。

11.  緩衝器容納的是普通位元組,為把他們轉換成字元,要麼在輸入時編碼,或輸出時解碼(CharSet類),才能呼叫asCharBuffer()列印輸出。P554

12.  獲取基本型別ByteBuffer bb = ByteBuffer.allocate(BSize);  bb.asCharBuffer().put(“Hi”);(用wrap()也行) 可以通過getChar()逐個讀取,其他基本型別類似。其中的asCharBuffer()稱為檢視緩衝器,讓我們通過某個特定的基本資料型別的視窗檢視其底層的ByteBuffer

13.  bb.order(ByteOrder.BIG_ENDIAN)改變bb為大端儲存順序。

14.  記憶體對映檔案:    MappedByteBuffer out =

      newRandomAccessFile("test.dat", "rw").getChannel()

      .map(FileChannel.MapMode.READ_WRITE, 0,length);之後可通過put寫get讀。
        注意:對映檔案訪問獲得的效能比nio更好。

15.  壓縮類的使用:直接將輸出流封裝成GZIPOutputStream或ZipOutputStream,輸入流同理。其他操作全部都是I/O讀寫。

16.  jar程式可根據我們的選擇自動壓縮檔案。

17.  java的物件序列化將那些實現了Serializable介面的物件轉換成一個位元組序列,並能在以後將這個位元組序列完全恢復為原來的物件。永續性意味著一個物件的生存週期不