Thinking in Java讀書筆記

Thinking in Java讀書筆記(IO和併發除外)

1.1、類中所有的private方法都隱式地指定爲final的,由於其他類無法取用private方法,所以也就無法覆蓋它;private方法無法被重寫,繼承對應private的方法無效,private的屬性也無法拿到,但我們通常private的屬性有對應的get、set方法,通常是public的,所以子類繼承後可以調用方法來取得到父類的屬性。

1.2、final類禁止繼承,所以final類中所有的方法都隱式地指定爲final的,因此無法覆蓋他們,你也可以給他們添加final關鍵字,但這沒什麼意義。

1.3、java中所有方法都是通過動態綁定來實現多態的。

1.4、如果某個方法是靜態的,它的行爲就不具有多態性。所以構造器不具有多態性,他們實際上是static的,只不過該static聲明是隱式的。

2.1、在方法的作用域內(而不是其他類的作用域內)創建一個完整的類,這個類稱爲局部內部類(其實命名就和局部變量一樣,在方法中的變量叫局部變量)。

3.1、Set中,HashSet擁有最快的獲取元素的方式;如果存儲順序很重要,那麼使用TreeSet,它按照比較結果的升序保存對象,或者使用LinkedHashSet,它按照被添加的順序保存對象。

3.2、List中,基本的arrayList擅長隨機訪問元素,但是在中間插入和移除元素時會比較慢;LinkedList和arrayList相反,中間插入和刪除快,隨機訪問慢。

3.3、ListIterator是一個更加強大的迭代器,但看名字就知道只用於List類,普通的Iterator只能向前移動,就只有hasNext(),但ListIterator可以雙向移動,有hasNext()和hasPrevious()兩種移動方式。

3.4、Queue是隊列,典型的先進先出容器,即從一端進去,另一端出來,取出順序和放入順序一樣。

3.5、java容器實際上就四種:Map、List、Set和Queue。下面詳細地介紹一下各個容器類型的性能特點區別:

3.5.1、List:元素可以重複。

ArrayList:底層是由數組支持的;

LinkedList:是由雙向鏈表實現的,其中的每一個對象都包含數據的同時還包含了指向鏈表中前一個和後一個元素的引用。

因此如果要經常在表中插入和刪除元素,LinkedList比較合適。

3.5.2、Set:元素必須唯一,不允許重複,是否重複由equals()方法判定,所以加入set的元素必須定義equals()方法以確保對象的唯一性。

HashSet:爲快速查找而設計的Set,其中的元素必須定義hashCode()方法,一般情況下都用他。

Treeset:保存了次序的set,底層爲(平衡)樹結構,使用他可以從Set中提取有序的序列,其中的元素必須實現Comparable接口(實現裏面的compareTo方法)。SortedSet是TreeSet的唯一實現。

LinkedHashSet:具有HashSet的查詢速度,使用鏈表來維護元素的順序(這個順序就是插入的次序),所以使用迭代器遍歷他時,結果就是插入的次序,元素必須定義hashCode()方法。

3.5.3、map:保存的是鍵值對,key不能重複,是唯一的,可以但只能有一個爲null,value可重複,可爲null。

HashMap:是map基於散列表的實現(現在已經取代了HashTable)。插入和查詢鍵值對的開銷是固定的,可以通過構造器設置容量和負載因子,以調整容器的性能。通常我們都使用他。

LinkedHashMap:在HashMap的基礎上保存了鍵值對插入的次序,訪問速度比hashMap慢一點,但迭代訪問時反而更快,因爲內部使用鏈表維護內部次序。

TreeMap:基於紅黑樹的實現,查看鍵或者鍵值對時,他們會被排序(次序由Comparable或者Comparator決定),因此特點就是結果是排過序的,他是唯一一個具有subMap()方法的map,可以返回一個子樹。SortedMap是TreeMap現階段的唯一實現。

ConcurrentHashMap:一種線程安全的map,它不涉及同步加鎖,卻比HashTable或者通過加鎖來實現同步的Collections.synchronizedMap(Map)得到的同步map性能更好。Collections.synchronizedMap(Map)是把一個不同步的map作爲參數返回一個同步的,靠加鎖來實現的。

WeakHashMap:弱鍵映射,允許釋放映射所指向的對象,爲解決特色問題而設計的。

IdentityHashMap:使用==代替equals()對“鍵”進行比較的散列映射,專爲解決特殊問題而設計。

3.6、Object.hashCode()方法生成散列碼,而它默認使用對象的地址計算散列碼,除非重寫他;Object.equals()默認比較對象的地址是否相同,除非你重寫他。

3.7、hashmap的結構我們通常叫他散列桶,散列表中的槽位通常稱爲桶位,因此實際散列表的數組命名爲bucket。

3.8、設計hashCode()時,最重要的因素是,無論何時,對同一個對象調用hashCode()都應該返回同樣的值,當然這個對象內的屬性不應該發生了變化。

3.9、java容器採用快速報錯機制,他會探查容器上的任何除了你的進程所進行的操作以外的所有變化,一旦發現其他進程修改了容器,就會立刻拋出ConcurrentModificationException異常。一個簡單的例子就可以看出快速報錯的原理:

public static void main(String[ args){

Collection<String> c = new ArrayList<String>();

Iterator<String> it = c.iterator();

c.add("An Object");

try{

String s = it.next();

}catch(ConcurrentModificationException e){

System.out.println(e);

}

}

//輸出:java.util.ConcurrentModificationException。。。

以上程序在運行時出現異常,catch後輸出了異常信息,因爲在容器取得迭代器後,又有東西放入到了容器中。當程序的不同部分修改了同一個容器時,就可能導致容器的狀態不一致,就出現了這個異常。此例中,迭代器在獲取容器後也可能會有修改容器的操作(比如iterator.remove()操作),所以在獲取迭代器後,不能再調用容器的add方法改變容器,應該在獲取迭代器之前就添加好元素。

3.10、在3.5中也提到了Hashtable,它和HashMap相似,甚至方法名也相似,Hashtable是同步的,但HashMap的同步問題也可通過Collections的一個靜態方法得到解決: Map Collections.synchronizedMap(Map m) 這個方法返回一個同步的Map,這個Map封裝了底層的HashMap的所有方法,使得底層的HashMap即使是在多線程的環境中也是安全的。 所以沒有理由再使用Hashtable而不用HashMap了。

4.1、Throwable對象可分爲兩種類型(指從Throwable繼承來的類型):Error用來表示編譯時和系統錯誤(除特殊情況外,一般不用你關心);Exception是可以拋出的基本類型。

4.2、因爲finally子句一定會執行,即便在try子句中return,也會繼續執行finally子句。

5.1、String對象是不可變的,String類中每一個看起來會修改String值的方法,實際上都是創建一個全新的String對象,最初的String對象則絲毫不動。

6.1、java泛型是使用擦除來實現的,這意味着當你在使用泛型時,任何具體的類型信息都被擦除了,你唯一知道的就是你在使用一個對象。因此List<String>和List<Integer>在運行時事實上是相同的類型,都被擦除成他們的“原生”類型--List。理解擦除以及如何處理它,是你在學習java泛型時面臨的最大障礙。如下:

Class c1 = new ArrayList<String>().getClass();

Class c2 = new ArrayList<Integer>().getClass();

System.out.println(c1 == c2);//輸出:true

同樣的因爲擦除,同一個類就不能實現同一個泛型接口的兩種變體,如下:

interface A<T> {}

Class B implements A<Integer> {}

Class C extends B implements A<String> {}

類C不能編譯,A<Integer>和A<String>因爲擦除而簡化爲相同的類型A,這樣C就重複實現了接口A兩次了。

但是有趣的是,如果把A的泛型參數去掉,編譯是可以的。如下:

 

interface A {}

Class B implements A {}

Class C extends B implements A {}

類C可以編譯。

6.2、java泛型的限制之一:不能將基本類型用作泛型的類型參數,如ArrayList<int>之類的是不能創建的。

7.1、編譯器會爲你創建的enum都繼承自java.lang.Enum。

7.2、枚舉的values()方法返回enum實例的數組,而且該數組中的元素嚴格保持在enum中的聲明順序。有趣的是,你的enum中沒有values()方法,你會以爲是編譯器會爲你創建enum時繼承了Enum,values方法應該在Enum這個父類裏。可惜,如果你研究一下Enum類就發現也不是,Enum裏也沒有。答案是,values()方法是由編譯器添加的static方法,是編譯器在創建enum時加進去的。

7.3、ordinal()方法返回一個int值,這是每個enum實例在聲明時的次序,從0開始。

7.4、對應enum實例可以直接使用==比較,編譯器會自動爲你提供equals()和hashcode()方法。

7.5、我們也知道所有的enum都會繼承自java.lang.Enum,因爲java不支持多重繼承,所以你的enum不能再繼承其他類了。

enum EnumA extends ClassB{...}這是不行的。

7.6、enum還有一個非常有趣的特性,就是它允許你爲enum實例編寫方法,從而爲每個enum實例賦予各自不同的行爲。大致步驟就是在enum中定義抽象方法,然後每個實例都實現它。如下:

public enum EnumTest{

SHI_LI_ONE {

String getInfo(){

return "實例1";

}

},

 

SHI_LI_TWO {

String getInfo(){

return "實例2";

}

};

abstract String getInfo();

}

8.1、switch語句是可以沒用default子句的,當表達式的值和所有值都不同時就退出了switch語句,繼續執行後面的。

9.1、註解也被稱之爲元數據,它爲我們在代碼裏添加信息提供了一種形式化的方法。註解的定義用@interface。例如:

public @interface Test{}

9.2、註解不支持繼承,不能使用extends來繼承某個@interface。

10.1、我們都知道static修飾的變量存放在方法區中,和類信息存放在一起,所以只有一份,可以用它來修飾作爲多線程任務區分的標識ID,例如每創建一個任務就加1等。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章