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

《Java程式設計思想》讀書筆記

2018.06.05 重新梳理下java基礎,熟悉的章節不記錄了。

第五章 初始化與清理

一、過載

定義:為了讓方法名相同而形參不同的構造器同時存在,即同名不同參。
區分:通過引數型別的差異。(注意:不要用返回值區分過載方法
型別提升(向上提升):
int — long — float — double
byte — short — int
char — int
窄化轉換:和向上提升反過來,注意先考慮降到byte再考慮char

二、this關鍵字、static方法

1、this關鍵字只能在方法內部使用,表示對“呼叫方法的那個物件”的引用。
2、static方法就是沒有this的方法,可以在沒有建立任何物件前提下,僅僅通過類本身來呼叫static方法,很像全域性方法。

三、清理:終結處理和垃圾回收

垃圾回收器只知道釋放那些經由new分配的記憶體。

(1)物件可能不被垃圾回收。

(2)垃圾回收不等於“析構”。

(3)垃圾回收只與記憶體有關(使用垃圾回收器的唯一原因就是回收程式不再使用的記憶體)。

1、finalize()的用途

(1)finalize()只處理一個特殊情況:通過某種建立物件方式以外的方式為物件分配了儲存空間。
finalize()用法比較像C中呼叫了malloc()來分配儲存空間,且必須呼叫free()才能使空間得到釋放,否則造成記憶體洩漏,那麼finalize()就是需要在本地方法中呼叫它,即呼叫C++的方法。
本地方法:就是在java中呼叫非java程式碼的方式。(面試題常考)


不應該將finalize()作為統一的清理方法,因為它可能不被執行,這是一個陷阱。
(2)finalize()還有一個有趣的用法,它並不依賴於每次都要對finalize()進行呼叫,這就是物件“終結條件”的驗證。
System.gc() 用於強制進行終結動作。

2、垃圾回收如何工作(面試經典題)
Java虛擬機器採用一種自適應的垃圾回收技術。

要是沒有新垃圾及產生,就會轉換到 "標記——清掃"工作模式。

"標記——清掃"所依據的思路同樣是從堆疊和靜態儲存區出發,遍歷所有的引用,進而找出存活的物件,給物件一個標記。全部標記工作完成後,清理動作才會開始。

“停止——複製”的回收動作不是在後臺執行的,它發生時,程式將會被暫停。它將所有活物件從舊堆複製到新堆,然後再釋放舊堆中的物件所佔記憶體。

如果所有物件都很穩定,垃圾回收器的效率降低,就切換到“標記——清掃”方式,同樣,Java虛擬機器會追蹤“標記——清掃”的效果,如果堆空間出現很多碎片,就會切換回“停止——複製”方式。

Java虛擬機器中有很多附加技術提升速度,比如“即時”編譯器技術(JIT)。這種技術將程式全部或部分翻譯成本地機器碼。

四、初始化

1、初始化成員

type value
boolean false
char [ ]
byte 0
short 0
int 0
long 0
float 0.0
double 0.0

2、靜態資料初始化
靜態資料只佔用一份儲存區域。靜態初始化只有在必要時候進行。只有在第一個型別物件建立(或第一次訪問靜態資料)的時候,他們才會被初始化。此後,靜態物件不會再被初始化。(在構造器之前)

初始化順序:
靜態物件(只一次)——> 非靜態物件

3、陣列初始化

(1)注意陣列不能直接賦值給另一個數組,要想複製陣列,要麼將元素遍歷一個個賦值,要麼用深拷貝(List、Map都是這樣)。

int[] a1 = { 1,2,3,4,5 };
int[] a2;
a2 = a1;

這樣是不行的,a2只是a1的一個引用a2的值改變相當於a1改變。
(2)可變引數列表
所謂可變引數列表,可以理解為函式的引數列表中某型別的數量是不確定的。類似於C中的varargs和python中的List。

五、列舉類

enum的一些特性:
1、toString() 函式可以方便的顯示某個例項的名字。
2、ordinal() 函式可以顯示某個特定enum常量的申明順序。
3、列舉型別可以配合switch case使用。
第2、3點在Android中用handler傳遞訊息有很多的應用。

第七章 複用類

一、繼承

當建立一個匯出類物件時,該物件包含了一個基類子物件。當初始化時,構造器的呼叫遵循由內到外的順序,預設情況下呼叫基類的無參構造器,基類構造器都有參時,可以用super(“引數”)顯式的呼叫基類構造器。

二、代理

就是使用JavaBean的原理

三、protected關鍵字

關鍵字protected表明,就類使用者而言,它是private的,就 繼承自此類的匯出類 或者 其它位於同一個包的類來說,它是可以訪問的。

四、final關鍵字

1、final資料:
(1)典型的定義常量:

public static final int Value = 66;

定義為public,則可以被用於包之外;定義為static,則強調只有一份;定義為final,則說明它是一個常量。
(2)定義為static和不定義為static區別

private final int i4 = rand.nextInt(20);
static final int i5 = rand.nextInt(20);

i5的值不可以通過建立第二個物件加以改變,因為它是static的,在裝載時已被初始化。
(3)final引用的意義
不能因為一個變數是final的,就認為無法改變它的值。由於它是一個引用,final意味著無法將該變數再次指向另一個新的物件。
2、final引數
在函式引數列表中的final引數,在函式內無法修改它。
3、final方法
使用場景:
1、把方法鎖定,防止繼承類修改,覆蓋它。
2、提高效率。(逐漸淘汰)
4、final類
使用場景:不打算整合該類,對該類的設計永不需要做任何變動,或出於安全考慮,不希望它有子類。

第八章 多型

“封裝”:利用抽象資料型別將資料和基於資料的操作封裝在一起,使其構成一個不可分割的獨立實體,資料被保護在抽象資料型別的內部,儘可能地隱藏內部的細節,只保留一些對外介面使之與外部發生聯絡。(例:JavaBean)
“繼承”:繼承是使用已存在的類的定義作為基礎建立新類的技術,新類的定義可以增加新的資料或新的功能,也可以用父類的功能,但不能選擇性地繼承父類。通過使用繼承我們能夠非常方便地複用以前的程式碼,能夠大大的提高開發的效率。
“多型”:消除型別之間的耦合關係。將改變的事物與未變的事物分離開來

一、向上轉型

子類物件的引用向上轉型為基類,傳遞到相應方法中。
多型的運作方式:只寫一個方法,這個方法僅接收基類作為引數,也就是說,不管匯出類的存在,編寫的程式碼只是與基類打交道。

二、轉機

1、繫結
將一個方法呼叫同一個方法主體關聯起來被稱作繫結。
為什麼需要?因為Java中所有方法都是通過動態繫結實現多型。
2、產生正確的行為
傳送訊息給某個物件,讓該物件去斷定應該做什麼事。
這裡寫圖片描述
向上轉型就是這句程式碼:

Shape s = new Circle();

3、可擴充套件性
所有方法只與基類介面通訊,這樣的程式可擴充套件,因為可以從通用的基類繼承出新的資料型別,從而新添一些功能。

三、構造器和多型

為什麼編譯器強制必須呼叫構造器,沒有指定基類某個構造器,也要呼叫預設構造器?(構造器的作用)
基類構造器用來對自己的元素進行初始化,因此必須令所有過早起都得到呼叫,否則不可能正確構造物件。
1、構造器的呼叫順序
基類構造器 -> 成員的初始化方法 -> 子類構造器
2、繼承與清理
在銷燬時,需要顯式的呼叫基類的dispose()方法,銷燬的順序和初始化相反,包括欄位的銷燬順序和申明的順序相反。
3、構造器內部的多型方法行為(會存在潛在的bug)
在初始化時,基類構造器中呼叫子類中覆蓋的方法,此時子類的成員變數未賦值,如果此時對其操作可能會產生意想之外的結果。所以應該避免這樣做。在構造器內唯一能夠安全呼叫的事基類中的final方法。(P163的例子很能說明問題)
4、用繼承進行設計
原則:(1)組合更加靈活,首選組合。
(2)用繼承表達行為間的差異,用欄位表達狀態上的變化。
例子:

import static net.mindview.util.Print.*;
class Actor {
public void act() {}
}
class HappyActor extends Actor {
public void act() { print("HappyActor"); }
}
class SadActor extends Actor {
public void act() { print("SadActor"); }
}
class Stage {
private Actor actor = new HappyActor();
public void change() { actor = new SadActor(); }
public void performPlay() { actor.act(); }
}
public class Transmogrify {
public static void main(String[] args) {
Stage stage = new Stage();
stage.performPlay();
stage.change();
stage.performPlay();
}
} /* Output:
HappyActor
SadActor

5、向下轉型與執行時型別識別
使用場景:
對於子類擴充套件基類介面的情況,向上轉型後不能呼叫子類的不同於基類的新方法。
這樣需要用到向下轉型,在Java中,所有的轉型都會對其進行檢查。稱為“執行時型別識別”(RTTT)如果轉型正確,則轉型成功;如果所轉型別不是正確的型別,則轉型失敗,返回ClassCastException異常。
例子:

class Useful {
public void f() {}
public void g() {}
}
class MoreUseful extends Useful {
public void f() {}
public void g() {}
public void u() {}
public void v() {}
public void w() {}
}
public class RTTI {
public static void main(String[] args) {
Useful[] x = {
new Useful(),
new MoreUseful()
};
x[0].f();
x[1].g();
// Compile time: method not found in Useful:
//! x[1].u();
((MoreUseful)x[1]).u(); // Downcast/RTTI
((MoreUseful)x[0]).u(); // Exception thrown
}
}

第九章 介面

一、抽象類和抽象方法

1、包含抽象方法的類叫抽象類。如果一個類包含一個或多個抽象方法,該類必須被限定為抽象的。
2、如果從一個抽象類繼承,並想建立該新類的物件,那麼必須為基類中的所有抽象方法提供方法定義。
3、可以建立一個沒有任何抽象方法的抽象類。(什麼情景下出現這種情況?

二、介面

interface這個關鍵字替代class關鍵字,產生了一個完全抽象的類。介面只提供形式,未提供任何具體實現。
介面被用了建立類與類之間的協議。介面也可以包含域,但是這些域隱式的是static和final的。因此,其中定義的成員變數,是static&final的。

介面與抽象類的區別:
a.繼承抽象類的子類們的本質都是相似的,它們體現的是一種 “is-a" 的關係,就像動物中的貓和狗;而對於介面的繼承更多的是一種行為的相似,是一種 “like-a” 的關係,比如飛機和鳥,它們都具有飛的行為,卻並不需要在本質上相似。
b.抽象類可以擁有任意範圍的成員資料,既可以是抽象,也可以是非抽象;但是介面,所有的方法必須是抽象的,所有的成員變數必須是 public static final的,某種程度上來說,介面是對抽象類的一種抽象。
c.一個類只能繼承一個抽象類,但卻可以實現多個介面。
d. 抽象的子類可以選擇性實現其基類的抽象方法 介面的子類必須實現

使用情況:
a.抽象類 和 介面 都是用來抽象具體物件的. 但是介面的抽象級別最高
b.抽象類可以有具體的方法 和屬性, 介面只能有抽象方法和不可變常量
c.抽象類主要用來抽象類別,介面主要用來抽象功能.
d.抽象類中,且不包含任何實現,派生類必須覆蓋它們。介面中所有方法都必須是未實現的。
e.介面是設計的結果 ,抽象類是重構的結果

三、完全解耦

1、策略模式:建立一個能夠根據所傳遞的引數物件的不同而具有不同行為的方法。
2、介面卡模式:介面卡中的程式碼將接受你說擁有的介面,併產生你所需要的介面。說白了就是橋接兩個介面,用與介面之間的轉換。
將介面從具體實現中解耦使得介面可以應用於多種不同的具體實現,因此程式碼更具有可複用性。

四、多重繼承

多重繼承使用介面的核心原因:為了能夠向上轉型為多個基型別。

五、通過繼承擴充套件介面

通過繼承,可以在介面中新增新的方法宣告,還可以通過繼承在新介面中組合數個介面。

六、適配介面

為什麼使用介面而不是類?
因為在介面卡模式中,我們可以在任何現有類之上新增新的介面,所以這意味著讓方法接受藉口型別,是一種讓任何類都可以對該方法進行適配的方式。

比如Scanner類。Scanner構造器接受一個Readable介面,如果建立了一個新類,並且想讓Scanner可以作用它,就讓它成為Readable,即實現Readable介面,這個介面只要求實現read()方法,新類實現這個方法就ok。·

七、介面與工廠

對消費者傳遞一個工廠1物件,產生工廠1的產品,呼叫產品1的方法。
對消費者傳遞一個工廠2物件,產生工廠2的產品,呼叫產品2的方法。

import static net.mindview.util.Print.*;
interface Service {
void method1();
void method2();
}
interface ServiceFactory {
Service getService();
}
class Implementation1 implements Service {
Implementation1() {} // Package access
public void method1() {print("Implementation1 method1");}
public void method2() {print("Implementation1 method2");}
}
class Implementation1Factory implements ServiceFactory {
public Service getService() {
return new Implementation1();
}
}
class Implementation2 implements Service {
Implementation2() {} // Package access
public void method1() {print("Implementation2 method1");}
public void method2() {print("Implementation2 method2");}
}
class Implementation2Factory implements ServiceFactory {
public Service getService() {
return new Implementation2();
}
}
public class Factories {
public static void serviceConsumer(ServiceFactory fact) {
Service s = fact.getService();
s.method1();
s.method2();
}
public static void main(String[] args) {
serviceConsumer(new Implementation1Factory());
// Implementations are completely interchangeable:
serviceConsumer(new Implementation2Factory());
}
} /* Output:
Implementation1 method1
Implementation1 method2
Implementation2 method1
Implementation2 method2

第十章 內部類

一、建立內部類

內部類是嵌在外部類內部的類。
如果想從外部類的非靜態方法建立一個內部類物件,一定要用這樣呼叫:OuterClassName . InnerClassName

Parcel2 q = new Parcel2();
Parcel2.Contents c = q.contents();

二、連結到外部類

內部類擁有其外圍類的所有元素的訪問權。
一個應用到迭代器模式的例子:

interface Selector{
    boolean end();
    Object current();
    void next();
}

public class Sequence {
    private Object[] items;
    private int next = 0;
    public Sequence(int size){
        items = new Object[size];
    }
    public void add(Object x){
        if ( next < items.length){
            items[next++] = x;
        }
    }

    private class SequenceSelector implements Selector{

        private int i = 0;
        @Override
        public boolean end() {
            return i == items.length;//內部類可以訪問成員
        }

        @Override
        public Object current() {
            return items[i];
        }

        @Override
        public void next() {
            if ( i < items.length){
                i++;
            }
        }

    }

    public Selector selector(){
        return new SequenceSelector();
    }

    public static void main(String[] arg0){
        Sequence sequence = new Sequence(10);
        for (int i = 0;i < 10;i++){
            sequence.add(i);
        }
        Selector selector = sequence.selector();//通過呼叫selector()獲取一個自己定義的Selector
        while ( !selector.end()){
            System.out.println(selector.current() + " ");
            selector.next();
        }
    }


}

三、在方法和作用域內的內部類

可以在一個方法或者任意的作用域內定義內部類,這樣做有兩個理由:
1、實現了某型別的介面,於是可以建立並返回對其的引用。
2、要解決一個複雜的問題,想建立一個類來輔助解決方案,但是不希望這個類是公用的。

eg1:在方法內定義內部類

public Destination destination(String s) { 
           class PDestination implements Destination { 

            private String label; 
            private PDestination(String whereTo) { 
               label = whereTo; 
            } 
            public String readLabel() { return label; } 
          } 
          return new PDestination(s); 
       } 

eg2:在作用域內定義內部類

public class Parcel6{
    private void internalTracking( boolean b){
        if (b){
            class TrackingSlip{
                private String id;
                TrackingSlip(String s){
                    id = s;
                }
                String getSlipe(){
                    return id;
                }
            }

            TrackingSlip ts = new TrackingSlip("slip");
            String s = ts.getSlipe();
            System.out.println(s);

        }
        //在if外面用是無效的,超出了作用域範圍
        //! TrackingSlip ts = new TrackingSlip("x");
    }
    public void track(){
        internalTracking(true);
    }
    public static void main(String[] arg0){
        Parcel6 parcel6 = new Parcel6();
        parcel6.track();
    }
}

四、匿名內部類

注意點:
1、如果定義一個匿名內部類,它需要使用外部定義的引數,那麼次引數引用需要是final的。

public class Parcel9 { 
       // Argument must be final to use inside 
       // anonymous inner class: 
       public Destination destination(final String dest) { 
          return new Destination() { 
            private String label = dest; 
            public String readLabel() { return label; } 
          }; 
       } 
       public static void main(String[] args) { 
          Parcel9 p = new Parcel9(); 
          Destination d = p.destination("Tasmania"); 
       } 
     }

2、如果這個引數並未直接被直接使用在匿名內部類內部(如被基類構造器使用),那麼就不需要是final的。

abstract class Base { 
       public Base(int i) { 
          print("Base constructor, i = " + i); 
       } 
       public abstract void f(); 
     } 


     public class AnonymousConstructor { 
       public static Base getBase(int i) { 
          return new Base(i) { 
            { print("Inside instance initializer"); } 
            public void f() { 
               print("In anonymous f()"); 
            } 
          }; 
       } 
       public static void main(String[] args) { 
          Base base = getBase(47); 
          base.f(); 
       } 
     }

上例中解決了一個問題:雖然匿名內部類不能有構造器,因為連名字都沒有,但可以先例項初始化,就能達到為匿名內部類建立一個構造器的效果。
3、可以更好的應用在工廠模式中

五、巢狀類

1 、將內部類宣告為static,則內部類物件與其外圍類物件之間不會有聯絡,這就叫做巢狀類。
巢狀類意味著:
1)、要建立巢狀類的物件,不需要其外圍類的物件。
2)、不能從巢狀類的物件中訪問非靜態的外圍物件。

2、介面內部的類
介面中的任何類自動是public和static的。
如果想建立某些公共程式碼,使得它們可以被某個介面的所有不同實現所公用,那麼使用介面內部的巢狀類會顯得很方便。

public interface ClassInInterface { 
       void howdy(); 
       class Test implements ClassInInterface { 
          public void howdy() { 
             System.out.println("Howdy!"); 
          } 
          public static void main(String[] args) { 
             new Test().howdy(); 
          } 
       } 
     } 

3 、為什麼需要內部類
每個內部類都能獨立地繼承自一個(介面的)實現,所以無論外圍類是否已經繼承了某個(介面的)實現,對於內部類都沒有影響。
如果使用內部類,可以獲得一些特別的特性:

內部類可以有多個例項,每個例項都有自己的狀態資訊,並且與其外圍類物件的資訊相互獨立。
在單個外圍類中,可以讓多個內部類以不同的方式實現同一個介面,或繼承同一個類。
建立內部類物件的時刻並不依賴於外圍類物件的建立。
內部類沒有令人迷惑的“is-a”關係,它就是一個獨立的實體。

六、閉包與回撥(很難理解)

所謂回撥:就是A類中呼叫B類中的某個方法C,然後B類中反過來呼叫A類中的方法D,D這個方法就叫回調方法
這裡寫圖片描述
有兩篇博文寫的非常好,不懂的時候可以反覆看:
1、https://blog.csdn.net/Allen_Zhao_2012/article/details/8056665
2、https://blog.csdn.net/xiaanming/article/details/8703708

七、內部類的繼承

class WithInner{
	class Inner(){}
}

public class InheritInner extends WithInner.Inner{
	InheritInner(WithInner wi){
		wi.super();
	}
	public static void main(String[] args){
		WithInner wi = new WithInner();
		InheritInner ii = new InheritInner(wi);
	}
}

InheritInner 只繼承內部類而不是外部類。但是當要生成一個構造器時,不能只是傳遞一個指向外圍類物件的引用。

八、區域性內部類

使用區域性內部類而不用匿名內部類的理由:
1、需要一個已命名的構造器,或者需要過載構造器,而匿名內部類只能用於例項初始化;
2、需要不止一個該內部類物件。

第十一章 持有物件(Collection、List、Map)

一、基本分類

Java容器類類庫的用途是“儲存物件”,並將其劃分為兩個不同的概念:

1)Collection。一個獨立元素的序列,這些元素都服從一條或多條規則。List必須按照插入的順序儲存元素,而Set不能有重複元素。Queue按照佇列規則來確定物件產生的順序。

2)Map。一組成對的"鍵值對"物件,允許使用鍵值來查詢值。對映表允許我們使用另一個物件查詢某個物件,它也被稱為“關聯陣列”。

二、Collection

1、新增一組元素
方法1:Collection.addAll()方法執行起來很快,而且構建一個不包含元素的Collection,然後呼叫Collection.addAll()這種方式很方便,因此它是首選方式。

Collection<Integer> collection = new ArrayList<Integer>(Arrays.asList(1, 2, 3, 4, 5));
Integer[] moreInts = { 6, 7, 8, 9, 10 }; 
collection.addAll(Arrays.asList(moreInts));

方法2:Collection.addAll()只能接受另一個Collection物件作為引數,不如Arrays.asList()和Collections.addAll()靈活,這兩個方法取樣的都是可變引數列表。(注意Collection和Collections)

Collections.addAll(collection, 11, 12, 13, 14, 15); 

2、各類容器的特點
Collection在每個槽中只能儲存一個元素。此類容器包括:List,它以特定的順序儲存一組元素;
Set,元素不能重複;
Queue,一端插入物件,另一端移除物件。
Map在每個槽內儲存兩個物件,即鍵和與之相關聯的值。
Collection打印出來的內容用[ ]括住,Map打印出來的內容用{ }括住。

對於Set:
HashSet:獲取元素方式最快(原理後續章節會講);
TreeSet:按照比較結果的升序儲存物件;
LinkedHashSet:按照被新增的順序儲存物件。

對於Map:
HashMap:提供了最快的查詢技術;
TreeMap:按照比較結果升序儲存鍵;
LinkedHashMap:按照插入順序儲存鍵,同時還保留了HashMap的查詢速度。

三、List

兩種型別的List:

ArrayList:它長於隨機訪問元素,但是在List的中間插入和移除元素時較慢;
LinkedList:它提供了代價較低的在List中間進行的插入和刪除操作,提供了優化的順序訪問。LinkedList在隨機訪問方面相對比較慢。
LinkedList在執行某些操作時比ArrayList更高效,但是在隨機訪問操作方面卻要遜色一些。
LinkedList還添加了可以作為棧,佇列,雙端佇列的方法。可以使用它們的方法。

indexOf()方法返回索引,如果重複元素存在,再新增一個相同的元素,呼叫該方法返回“-1”(表示未找到它),此時呼叫remove()返回false。

subList()方法允許很容易的從較大的列表中建立一個片段,順序並不影響containsAll()的判斷結果。

retainAll()保留兩List的交集。

removeAll()也是基於equals()方法的。

四、迭代器

迭代器(Iterator,一種設計模式)是一個物件,它的工作是遍歷並選擇序列中的物件,迭代器通常被稱為輕量級物件,建立它們的代價小。

Java的Iterator只能單向移動,用來:

1、使用方法Iterator()要求容器返回一個Iterator。Iterator準備好返回序列的第一個元素。
2、使用next()獲得序列中的下一個元素。
3、使用hasNext()檢查序列中是否還有元素。
4、使用remove()將迭代器新近返回的元素刪除。
在remove()之前必須先呼叫next()。

五、Stack

“棧”通常是指“後進先出”(LIFO)的容器。
可以直接將LinkedList當做棧使用。

 public class Stack<T> { 
       private LinkedList<T> storage = new LinkedList<T>(); 
       public void push(T v) { storage.addFirst(v); } 
       public T peek() { return storage.getFirst(); } 
       public T pop() { return storage.removeFirst(); } 
       public boolean empty() { return storage.isEmpty(); } 
       public String toString() { return storage.toString(); } 
     }

Push()接受T型別物件,peek()和pop()返回T型別物件。
peek()方法將提供棧頂元素,但並不將其從棧頂移除。
pop()將移除並返回棧頂元素。

用Stack實現:

import net.mindview.util.*; 
     public class StackTest { 
       public static void main(String[] args) { 
          Stack<String> stack = new Stack<String>(); 
          for(String s : "My dog has fleas".split(" ")) 
             stack.push(s); 
          while(!stack.empty()) 
             System.out.print(stack.pop() + " "); 
       } 
     } /* Output: 
     fleas has dog My 

注意顯示的匯入import net.mindview.util.Stack; 而不是jiava.util.Stack。
如果想在自己的程式碼裡使用Stack類,在建立例項時,需要完整的指定包名,否則可能會和java.util包中的Stack發生衝突。

六、Set

特點:
1、Set不儲存重複元素。
2、HashSet使用了雜湊(雜湊概念後續章節有),因此輸出的順序沒有規律。
3、若想對結果排序,用TreeSet。

HashSet、TreeSet、LinkedHashSet
HashSet使用了雜湊,HashSet所維護的順序與TreeSet和LinkedHashSet都不一樣,因為他們的實現具有不同的元素儲存方式。TreeSet將元素儲存在紅-黑樹資料結構中,而HashSet使用的是雜湊函式。LinkedHashList因為查詢速度的原因也使用了雜湊,但是看起來是使用了連結串列來維護元素的插入順序。

七、Map

Map的get方法返回該鍵對應的值。如果沒有則返回null。
Map的put方法放入鍵值對。
Map的contansKey()返回是否含有這個鍵,containsValue()返回是否含有這個值。
KeySet()方法產生所有健組成的Set,可用於遍歷。
Map有四種取值方法,網上可以查到。

八、Queue

佇列是一個典型的先進先出(FIFO)的容器。佇列常被當做一種可靠的將物件從程式的某個區域傳輸到另一個區域的途徑。佇列在併發程式設計中特別重要。因為它們可以安全的將物件從一個任務傳輸給另一個任務。

offer()是與Queue相關的方法之一,他在允許的情況下,將一個元素插入到隊尾,或者返回false。
peek()和element()都將在布衣櫥的情況下返回隊頭,但是peek()方法在佇列為空時返回null,而element()會丟擲NoSuchElementException異常。
poll()和remove()方法將一出並返回隊頭,但是poll()在佇列為空時返回null,而remove()會丟擲NoSuchElementException異常。

總結:
這裡寫圖片描述

第十五章 泛型

作者曰:理解了邊界所在,你才能成為程式高手。因為只有知道了某個技術不能做到什麼,你才能更好地做到所能做的(部分原因是,不必浪費時間在死衚衕裡亂轉)。
一、元組
實現的功能:僅一次方法呼叫就返回多個物件
元組(tuple)將一組物件直接打包儲存於其中的一個單一物件。這個容器允許讀取其中的元素,但不允許向其中存放新的物件。

public class TwoTuple<A,B>{
	public final A first;
	public final B second;
	public TwoTuple(A a,B b){
		first = a;
		second = b;
	}

	public String toString(){
		return "(" + first + "," + second + ")";
	}
}

該例子的安全性:
1、客戶端程式讀取first和second物件,可以任意使用,但由於宣告為final,因此無法對first和second賦值
2、若想要使用不同元素的元組,強制要求他們另外建立一個新的TwoTuple物件。

二、泛型介面
1、泛型介面可應用於生成器,這是一種專門負責建立物件的類,一個生成器只定義一個方法,該方法用以產生新的物件。比如next()方法。如下:

public interface Generator<T>{
	T next();
}

2、泛型的一個侷限性:基本型別無法作為型別引數。比如int型別變數的型別引數為“Integer”。
3、可用於匿名內部類或內部類
這一章後面太細太複雜 不看了
---------分割線2018.10.31--------
突然發現草稿箱還有筆記。哎。。。好久沒用java了,以後有時間再把後續章節的內容整理好。