Java 高階程式設計
Java高階程式設計
1. Java資料結構
- 列舉(Enumeration)介面定義了一種從資料結構中取回連續元素的方式。
- 位集合(BitSet)實現了一組可以單獨設定和清除的位或標誌。
- 向量(Vector)和傳統陣列非常相似,但是Vector的大小能根據需要動態的變化。
- 棧(Stack)實現了一個後進先出(LIFO)的資料結構。
- 字典(Dictionary)是一個抽象類,它定義了鍵對映到值的資料結構。
- 雜湊表(Hashtable)提供了一種在使用者定義鍵結構的基礎上來組織資料的手段。
- 屬性(Properties)繼承於 Hashtable.Properties 類表示了一個持久的屬性集.屬性列表中每個鍵及其對應值都是一個字串。
Vector
Vector 類實現了一個動態陣列。和 ArrayList 很相似,但是兩者是不同的:
- Vector 是同步訪問的。
- Vector 包含了許多傳統的方法,這些方法不屬於集合框架。
Vector 主要用在事先不知道陣列的大小,或者只是需要一個可以改變大小的陣列的情況。
Stack
棧是Vector的一個子類,它實現了一個標準的後進先出的棧。
序號 | 方法描述 |
---|---|
1 | boolean empty() 測試堆疊是否為空。 |
2 | Object peek( ) 檢視堆疊頂部的物件,但不從堆疊中移除它。 |
3 | Object pop( ) 移除堆疊頂部的物件,並作為此函式的值返回該物件。 |
4 | Object push(Object element) 把項壓入堆疊頂部。 |
5 | int search(Object element) 返回物件在堆疊中的位置,以 1 為基數。 |
Hashtable類
Hashtable是原始的java.util的一部分, 是一個Dictionary具體的實現 。
然而,Java 2 重構的Hashtable實現了Map介面,因此,Hashtable現在整合到了集合框架中。它和HashMap類很相似,但是它支援同步。
像HashMap一樣,Hashtable在雜湊表中儲存鍵/值對。當使用一個雜湊表,要指定用作鍵的物件,以及要連結到該鍵的值。
2. Java集合框架
集合框架被設計成要滿足以下幾個目標。
- 該框架必須是高效能的。基本集合(動態陣列,連結串列,樹,雜湊表)的實現也必須是高效的。
- 該框架允許不同型別的集合,以類似的方式工作,具有高度的互操作性。
- 對一個集合的擴充套件和適應必須是簡單的。
從上面的集合框架圖可以看到,Java 集合框架主要包括兩種型別的容器,一種是集合(Collection),儲存一個元素集合,另一種是圖(Map),儲存鍵/值對對映。Collection 介面又有 3 種子型別,List、Set 和 Queue,再下面是一些抽象類,最後是具體實現類,常用的有 ArrayList、LinkedList、HashSet、LinkedHashSet、HashMap、LinkedHashMap 等等。
集合框架是一個用來代表和操縱集合的統一架構。所有的集合框架都包含如下內容:
- 介面:是代表集合的抽象資料型別。例如 Collection、List、Set、Map 等。之所以定義多個介面,是為了以不同的方式操作集合物件
- 實現(類):是集合介面的具體實現。從本質上講,它們是可重複使用的資料結構,例如:ArrayList、LinkedList、HashSet、HashMap。
- 演算法:是實現集合介面的物件裡的方法執行的一些有用的計算,例如:搜尋和排序。這些演算法被稱為多型,那是因為相同的方法可以在相似的介面上有著不同的實現。
集合框架體系圖
2.1 集合介面
序號 | 介面描述 |
---|---|
1 | Collection 介面 Collection 是最基本的集合介面,一個 Collection 代表一組 Object,即 Collection 的元素, Java不提供直接繼承自Collection的類,只提供繼承於的子介面(如List和set)。Collection 介面儲存一組不唯一,無序的物件。 |
2 | List 介面 List介面是一個有序的 Collection,使用此介面能夠精確的控制每個元素插入的位置,能夠通過索引(元素在List中位置,類似於陣列的下標)來訪問List中的元素,第一個元素的索引為 0,而且允許有相同的元素。List 介面儲存一組不唯一,有序(插入順序)的物件。 |
3 | Set Set 具有與 Collection 完全一樣的介面,只是行為上不同,Set 不儲存重複的元素。Set 介面儲存一組唯一,無序的物件。 |
4 | SortedSet 繼承於Set儲存有序的集合。 |
5 | Map Map 介面儲存一組鍵值物件,提供key(鍵)到value(值)的對映。 |
6 | Map.Entry 描述在一個Map中的一個元素(鍵/值對)。是一個 Map 的內部介面。 |
7 | SortedMap 繼承於 Map,使 Key 保持在升序排列。 |
8 | Enumeration 這是一個傳統的介面和定義的方法,通過它可以列舉(一次獲得一個)物件集合中的元素。這個傳統介面已被迭代器取代。 |
Set和List的區別
- Set 介面例項儲存的是無序的,不重複的資料。List 介面例項儲存的是有序的,可以重複的元素。
- Set檢索效率低下,刪除和插入效率高,插入和刪除不會引起元素位置改變 <實現類有HashSet,TreeSet>。
- List和陣列類似,可以動態增長,根據實際儲存的資料的長度自動增長List的長度。查詢元素效率高,插入刪除效率低,因為會引起其他元素位置改變 <實現類有ArrayList,LinkedList,Vector> 。
2.2 集合實現類(集合類)
序號 | 類描述 |
---|---|
1 | AbstractCollection 實現了大部分的集合介面。 |
2 | AbstractList 繼承於AbstractCollection 並且實現了大部分List介面。 |
3 | AbstractSequentialList 繼承於 AbstractList ,提供了對資料元素的鏈式訪問而不是隨機訪問。 |
4 |
LinkedList 該類實現了List介面,允許有null(空)元素。主要用於建立連結串列資料結構,該類沒有同步方法,如果多個執行緒同時訪問一個List,則必須自己實現訪問同步,解決方法就是在建立List時候構造一個同步的List。例如:List list=Collections.synchronizedList(newLinkedList(...)); LinkedList 查詢效率低。 |
5 | ArrayList 該類也是實現了List的介面,實現了可變大小的陣列,隨機訪問和遍歷元素時,提供更好的效能。該類也是非同步的,在多執行緒的情況下不要使用。ArrayList 增長當前長度的50%,插入刪除效率低。 |
6 | AbstractSet 繼承於AbstractCollection 並且實現了大部分Set介面。 |
7 | HashSet 該類實現了Set介面,不允許出現重複元素,不保證集合中元素的順序,允許包含值為null的元素,但最多隻能一個。 |
8 | LinkedHashSet 具有可預知迭代順序的 Set 介面的雜湊表和連結列表實現。 |
9 | TreeSet 該類實現了Set介面,可以實現排序等功能。 |
10 | AbstractMap 實現了大部分的Map介面。 |
11 | HashMap HashMap 是一個散列表,它儲存的內容是鍵值對(key-value)對映。 該類實現了Map介面,根據鍵的HashCode值儲存資料,具有很快的訪問速度,最多允許一條記錄的鍵為null,不支援執行緒同步。 |
12 | TreeMap 繼承了AbstractMap,並且使用一顆樹。 |
13 | WeakHashMap 繼承AbstractMap類,使用弱金鑰的雜湊表。 |
14 | LinkedHashMap 繼承於HashMap,使用元素的自然順序對元素進行排序. |
15 | IdentityHashMap 繼承AbstractMap類,比較文件時使用引用相等。 |
2.3 集合演算法
集合框架定義了幾種演算法,可用於集合和對映。這些演算法被定義為集合類的靜態方法。
在嘗試比較不相容的型別時,一些方法能夠丟擲 ClassCastException異常。當試圖修改一個不可修改的集合時,丟擲UnsupportedOperationException異常。
集合定義三個靜態的變數:EMPTY_SET,EMPTY_LIST,EMPTY_MAP的。這些變數都不可改變。
序號 | 演算法描述 |
---|---|
1 | Collection Algorithms 這裡是一個列表中的所有演算法實現。 |
2.4 迭代器
通常情況下,你會希望遍歷一個集合中的元素。例如,顯示集合中的每個元素。
一般遍歷陣列都是採用for迴圈或者增強for,這兩個方法也可以用在集合框架,但是還有一種方法是採用迭代器遍歷集合框架,它是一個物件,實現了Iterator 介面或 ListIterator介面。
迭代器,使你能夠通過迴圈來得到或刪除集合的元素。ListIterator 繼承了 Iterator,以允許雙向遍歷列表和修改元素。
Java Iterator(迭代器)不是一個集合,它是一種用於訪問集合的方法,可用於迭代 ArrayList 和 HashSet 等集合。
Iterator 是 Java 迭代器最簡單的實現,ListIterator 是 Collection API 中的介面, 它擴充套件了 Iterator 介面。
迭代器 it 的兩個基本操作是 next 、hasNext 和 remove。
呼叫 it.next() 會返回迭代器的下一個元素,並且更新迭代器的狀態。
呼叫 it.hasNext() 用於檢測集合中是否還有元素。
呼叫 it.remove() 將迭代器返回的元素刪除。
比較器
TreeSet和TreeMap的按照排序順序來儲存元素. 然而,這是通過比較器來精確定義按照什麼樣的排序順序。
這個介面可以讓我們以不同的方式來排序一個集合。
序號 | 比較器方法描述 |
---|---|
1 | 使用 Java Comparator 這裡通過例項列出Comparator介面提供的所有方法 |
總結
Java集合框架為程式設計師提供了預先包裝的資料結構和演算法來操縱他們。
集合是一個物件,可容納其他物件的引用。集合介面宣告對每一種型別的集合可以執行的操作。
集合框架的類和介面均在java.util包中。
任何物件加入集合類後,自動轉變為Object型別,所以在取出的時候,需要進行強制型別轉換。
3. Java ArrayList
ArrayList 類是一個可以動態修改的陣列,與普通陣列的區別就是它是沒有固定大小的限制,我們可以新增或刪除元素。
ArrayList 繼承了 AbstractList ,並實現了 List 介面。
其他引用型別
ArrayList 中的元素實際上是物件,在以上例項中,陣列列表元素都是字串 String 型別。
如果我們要儲存其他型別,而
基本型別對應的包裝類表如下:
基本型別 | 引用型別 |
---|---|
boolean | Boolean |
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
此外,BigInteger、BigDecimal 用於高精度的運算,BigInteger 支援任意精度的整數,也是引用型別,但它們沒有相對應的基本型別。
3.1 ArrayList方法
方法 | 描述 |
---|---|
add() | 將元素插入到指定位置的 arraylist 中 |
addAll() | 新增集合中的所有元素到 arraylist 中 |
clear() | 刪除 arraylist 中的所有元素 |
clone() | 複製一份 arraylist |
contains() | 判斷元素是否在 arraylist |
get() | 通過索引值獲取 arraylist 中的元素 |
indexOf() | 返回 arraylist 中元素的索引值 |
removeAll() | 刪除存在於指定集合中的 arraylist 裡的所有元素 |
remove() | 刪除 arraylist 裡的單個元素 |
size() | 返回 arraylist 裡元素數量 |
isEmpty() | 判斷 arraylist 是否為空 |
subList() | 擷取部分 arraylist 的元素 |
set() | 替換 arraylist 中指定索引的元素 |
sort() | 對 arraylist 元素進行排序 |
toArray() | 將 arraylist 轉換為陣列 |
toString() | 將 arraylist 轉換為字串 |
ensureCapacity() | 設定指定容量大小的 arraylist |
lastIndexOf() | 返回指定元素在 arraylist 中最後一次出現的位置 |
retainAll() | 保留 arraylist 中在指定集合中也存在的那些元素 |
containsAll() | 檢視 arraylist 是否包含指定集合中的所有元素 |
trimToSize() | 將 arraylist 中的容量調整為陣列中的元素個數 |
removeRange() | 刪除 arraylist 中指定索引之間存在的元素 |
replaceAll() | 將給定的操作內容替換掉陣列中每一個元素 |
removeIf() | 刪除所有滿足特定條件的 arraylist 元素 |
forEach() | 遍歷 arraylist 中每一個元素並執行特定操作 |
4. Java LinkedList
連結串列(Linked list)是一種常見的基礎資料結構,是一種線性表,但是並不會按線性的順序儲存資料,而是在每一個節點裡存到下一個節點的地址。
一個單向連結串列包含兩個值: 當前節點的值和一個指向下一個節點的連結。
一個雙向連結串列有三個整數值: 數值、向後的節點連結、向前的節點連結。
Java LinkedList(連結串列) 類似於 ArrayList,是一種常用的資料容器。與 ArrayList 相比,LinkedList 的增加和刪除對操作效率更高,而查詢和修改的操作效率較低。
以下情況使用 ArrayList :
- 頻繁訪問列表中的某一個元素。
- 只需要在列表末尾進行新增和刪除元素操作。
以下情況使用 LinkedList :
- 你需要通過迴圈迭代來訪問列表中的某些元素。
- 需要頻繁的在列表開頭、中間、末尾等位置進行新增和刪除元素操作。
LinkedList 繼承了 AbstractSequentialList 類。
LinkedList 實現了 Queue 介面,可作為佇列使用。
LinkedList 實現了 List 介面,可進行列表的相關操作。
LinkedList 實現了 Deque 介面,可作為佇列使用。
LinkedList 實現了 Cloneable 介面,可實現克隆。
LinkedList 實現了 java.io.Serializable 介面,即可支援序列化,能通過序列化去傳輸。
4.1 LinkedList方法
方法 | 描述 |
---|---|
public boolean add(E e) | 連結串列末尾新增元素,返回是否成功,成功為 true,失敗為 false。 |
public void add(int index, E element) | 向指定位置插入元素。 |
public boolean addAll(Collection c) | 將一個集合的所有元素新增到連結串列後面,返回是否成功,成功為 true,失敗為 false。 |
public boolean addAll(int index, Collection c) | 將一個集合的所有元素新增到連結串列的指定位置後面,返回是否成功,成功為 true,失敗為 false。 |
public void addFirst(E e) | 元素新增到頭部。 |
public void addLast(E e) | 元素新增到尾部。 |
public boolean offer(E e) | 向連結串列末尾新增元素,返回是否成功,成功為 true,失敗為 false。 |
public boolean offerFirst(E e) | 頭部插入元素,返回是否成功,成功為 true,失敗為 false。 |
public boolean offerLast(E e) | 尾部插入元素,返回是否成功,成功為 true,失敗為 false。 |
public void clear() | 清空連結串列。 |
public E removeFirst() | 刪除並返回第一個元素。 |
public E removeLast() | 刪除並返回最後一個元素。 |
public boolean remove(Object o) | 刪除某一元素,返回是否成功,成功為 true,失敗為 false。 |
public E remove(int index) | 刪除指定位置的元素。 |
public E poll() | 刪除並返回第一個元素。 |
public E remove() | 刪除並返回第一個元素。 |
public boolean contains(Object o) | 判斷是否含有某一元素。 |
public E get(int index) | 返回指定位置的元素。 |
public E getFirst() | 返回第一個元素。 |
public E getLast() | 返回最後一個元素。 |
public int indexOf(Object o) | 查詢指定元素從前往後第一次出現的索引。 |
public int lastIndexOf(Object o) | 查詢指定元素最後一次出現的索引。 |
public E peek() | 返回第一個元素。 |
public E element() | 返回第一個元素。 |
public E peekFirst() | 返回頭部元素。 |
public E peekLast() | 返回尾部元素。 |
public E set(int index, E element) | 設定指定位置的元素。 |
public Object clone() | 克隆該列表。 |
public Iterator descendingIterator() | 返回倒序迭代器。 |
public int size() | 返回連結串列元素個數。 |
public ListIterator listIterator(int index) | 返回從指定位置開始到末尾的迭代器。 |
public Object[] toArray() | 返回一個由連結串列元素組成的陣列。 |
public T[] toArray(T[] a) | 返回一個由連結串列元素轉換型別而成的陣列。 |
5. Java HashSet
HashSet 基於 HashMap 來實現的,是一個不允許有重複元素的集合。
HashSet 允許有 null 值。
HashSet 是無序的,即不會記錄插入的順序。
HashSet 不是執行緒安全的, 如果多個執行緒嘗試同時修改 HashSet,則最終結果是不確定的。 您必須在多執行緒訪問時顯式同步對 HashSet 的併發訪問。
HashSet 實現了 Set 介面。
6. Java HashMap
HashMap 是一個散列表,它儲存的內容是鍵值對(key-value)對映。
HashMap 實現了 Map 介面,根據鍵的 HashCode 值儲存資料,具有很快的訪問速度,最多允許一條記錄的鍵為 null,不支援執行緒同步。
HashMap 是無序的,即不會記錄插入的順序。
HashMap 繼承於AbstractMap,實現了 Map、Cloneable、java.io.Serializable 介面。
6.1 Hashmap方法
方法 | 描述 |
---|---|
clear() | 刪除 hashMap 中的所有鍵/值對 |
clone() | 複製一份 hashMap |
isEmpty() | 判斷 hashMap 是否為空 |
size() | 計算 hashMap 中鍵/值對的數量 |
put() | 將鍵/值對新增到 hashMap 中 |
putAll() | 將所有鍵/值對新增到 hashMap 中 |
putIfAbsent() | 如果 hashMap 中不存在指定的鍵,則將指定的鍵/值對插入到 hashMap 中。 |
remove() | 刪除 hashMap 中指定鍵 key 的對映關係 |
containsKey() | 檢查 hashMap 中是否存在指定的 key 對應的對映關係。 |
containsValue() | 檢查 hashMap 中是否存在指定的 value 對應的對映關係。 |
replace() | 替換 hashMap 中是指定的 key 對應的 value。 |
replaceAll() | 將 hashMap 中的所有對映關係替換成給定的函式所執行的結果。 |
get() | 獲取指定 key 對應對 value |
getOrDefault() | 獲取指定 key 對應對 value,如果找不到 key ,則返回設定的預設值 |
forEach() | 對 hashMap 中的每個對映執行指定的操作。 |
entrySet() | 返回 hashMap 中所有對映項的集合集合檢視。 |
keySet() | 返回 hashMap 中所有 key 組成的集合檢視。 |
values() | 返回 hashMap 中存在的所有 value 值。 |
merge() | 新增鍵值對到 hashMap 中 |
compute() | 對 hashMap 中指定 key 的值進行重新計算 |
computeIfAbsent() | 對 hashMap 中指定 key 的值進行重新計算,如果不存在這個 key,則新增到 hasMap 中 |
computeIfPresent() | 對 hashMap 中指定 key 的值進行重新計算,前提是該 key 存在於 hashMap 中。 |
7. Object類
Java Object 類是所有類的父類,也就是說 Java 的所有類都繼承了 Object,子類可以使用 Object 的所有方法。
序號 | 方法 & 描述 |
---|---|
1 | protected Object clone()建立並返回一個物件的拷貝 |
2 | boolean equals(Object obj)比較兩個物件是否相等 |
3 | protected void finalize()當 GC (垃圾回收器)確定不存在對該物件的有更多引用時,由物件的垃圾回收器呼叫此方法。 |
4 | Class getClass()獲取物件的執行時物件的類 |
5 | int hashCode()獲取物件的 hash 值 |
6 | void notify()喚醒在該物件上等待的某個執行緒 |
7 | void notifyAll()喚醒在該物件上等待的所有執行緒 |
8 | String toString()返回物件的字串表示形式 |
9 | void wait()讓當前執行緒進入等待狀態。直到其他執行緒呼叫此物件的 notify() 方法或 notifyAll() 方法。 |
10 | void wait(long timeout)讓當前執行緒處於等待(阻塞)狀態,直到其他執行緒呼叫此物件的 notify() 方法或 notifyAll() 方法,或者超過引數設定的timeout超時時間。 |
11 | void wait(long timeout, int nanos)與 wait(long timeout) 方法類似,多了一個 nanos 引數,這個引數表示額外時間(以納秒為單位,範圍是 0-999999)。 所以超時的時間還需要加上 nanos 納秒。 |
8. Java泛型
Java 泛型(generics)是 JDK 5 中引入的一個新特性, 泛型提供了編譯時型別安全檢測機制,該機制允許程式設計師在編譯時檢測到非法的型別。
泛型的本質是引數化型別,也就是說所操作的資料型別被指定為一個引數。
8.1 泛型方法
你可以寫一個泛型方法,該方法在呼叫時可以接收不同型別的引數。根據傳遞給泛型方法的引數型別,編譯器適當地處理每一個方法呼叫。
下面是定義泛型方法的規則:
- 所有泛型方法宣告都有一個型別引數宣告部分(由尖括號分隔),該型別引數宣告部分在方法返回型別之前(在下面例子中的
)。 - 每一個型別引數宣告部分包含一個或多個型別引數,引數間用逗號隔開。一個泛型引數,也被稱為一個型別變數,是用於指定一個泛型型別名稱的識別符號。
- 型別引數能被用來宣告返回值型別,並且能作為泛型方法得到的實際引數型別的佔位符。
- 泛型方法體的宣告和其他方法一樣。注意型別引數只能代表引用型型別,不能是原始型別(像int,double,char的等)。
8.2 泛型類
泛型類的宣告和非泛型類的宣告類似,除了在類名後面添加了型別引數宣告部分。
和泛型方法一樣,泛型類的型別引數宣告部分也包含一個或多個型別引數,引數間用逗號隔開。一個泛型引數,也被稱為一個型別變數,是用於指定一個泛型型別名稱的識別符號。因為他們接受一個或多個引數,這些類被稱為引數化的類或引數化的型別。
8.3 型別萬用字元
1、型別萬用字元一般是使用?代替具體的型別引數。例如 List<?> 在邏輯上是List
2、型別萬用字元上限通過形如List來定義,如此定義就是萬用字元泛型值接受Number及其下層子類型別。
3、型別萬用字元下限通過形如 List<? super Number>來定義,表示型別只能接受Number及其三層父類型別,如 Object 型別的例項。
9. Java序列化
Java 提供了一種物件序列化的機制,該機制中,一個物件可以被表示為一個位元組序列,該位元組序列包括該物件的資料、有關物件的型別的資訊和儲存在物件中資料的型別。
將序列化物件寫入檔案之後,可以從檔案中讀取出來,並且對它進行反序列化,也就是說,物件的型別資訊、物件的資料,還有物件中的資料型別可以用來在記憶體中新建物件。
整個過程都是 Java 虛擬機器(JVM)獨立的,也就是說,在一個平臺上序列化的物件可以在另一個完全不同的平臺上反序列化該物件。
類 ObjectInputStream 和 ObjectOutputStream 是高層次的資料流,它們包含反序列化和序列化物件的方法。
請注意,一個類的物件要想序列化成功,必須滿足兩個條件:
該類必須實現 java.io.Serializable 介面。
該類的所有屬性必須是可序列化的。如果有一個屬性不是可序列化的,則該屬性必須註明是短暫的。
如果你想知道一個 Java 標準類是否是可序列化的,請檢視該類的文件。檢驗一個類的例項是否能序列化十分簡單, 只需要檢視該類有沒有實現 java.io.Serializable介面。
10. Java網路程式設計
網路程式設計是指編寫執行在多個裝置(計算機)的程式,這些裝置都通過網路連線起來。
java.net 包中 J2SE 的 API 包含有類和介面,它們提供低層次的通訊細節。你可以直接使用這些類和介面,來專注於解決問題,而不用關注通訊細節。
java.net 包中提供了兩種常見的網路協議的支援:
- TCP:TCP(英語:Transmission Control Protocol,傳輸控制協議) 是一種面向連線的、可靠的、基於位元組流的傳輸層通訊協議,TCP 層是位於 IP 層之上,應用層之下的中間層。TCP 保障了兩個應用程式之間的可靠通訊。通常用於網際網路協議,被稱 TCP / IP。
- UDP:UDP (英語:User Datagram Protocol,使用者資料報協議),位於 OSI 模型的傳輸層。一個無連線的協議。提供了應用程式之間要傳送資料的資料報。由於UDP缺乏可靠性且屬於無連線協議,所以應用程式通常必須容許一些丟失、錯誤或重複的資料包。
10.1 Socket程式設計
套接字使用TCP提供了兩臺計算機之間的通訊機制。 客戶端程式建立一個套接字,並嘗試連線伺服器的套接字。
當連線建立時,伺服器會建立一個 Socket 物件。客戶端和伺服器現在可以通過對 Socket 物件的寫入和讀取來進行通訊。
java.net.Socket 類代表一個套接字,並且 java.net.ServerSocket 類為伺服器程式提供了一種來監聽客戶端,並與他們建立連線的機制。
以下步驟在兩臺計算機之間使用套接字建立TCP連線時會出現:
- 伺服器例項化一個 ServerSocket 物件,表示通過伺服器上的埠通訊。
- 伺服器呼叫 ServerSocket 類的 accept() 方法,該方法將一直等待,直到客戶端連線到伺服器上給定的埠。
- 伺服器正在等待時,一個客戶端例項化一個 Socket 物件,指定伺服器名稱和埠號來請求連線。
- Socket 類的建構函式試圖將客戶端連線到指定的伺服器和埠號。如果通訊被建立,則在客戶端建立一個 Socket 物件能夠與伺服器進行通訊。
- 在伺服器端,accept() 方法返回伺服器上一個新的 socket 引用,該 socket 連線到客戶端的 socket。
連線建立後,通過使用 I/O 流在進行通訊,每一個socket都有一個輸出流和一個輸入流,客戶端的輸出流連線到伺服器端的輸入流,而客戶端的輸入流連線到伺服器端的輸出流。
TCP 是一個雙向的通訊協議,因此資料可以通過兩個資料流在同一時間傳送.以下是一些類提供的一套完整的有用的方法來實現 socket。
10.2 ServerSocket類的方法
構造方法:
序號 | 方法描述 |
---|---|
1 | public ServerSocket(int port) throws IOException 建立繫結到特定埠的伺服器套接字。 |
2 | public ServerSocket(int port, int backlog) throws IOException 利用指定的 backlog 建立伺服器套接字並將其繫結到指定的本地埠號。 |
3 | public ServerSocket(int port, int backlog, InetAddress address) throws IOException 使用指定的埠、偵聽 backlog 和要繫結到的本地 IP 地址建立伺服器。 |
4 | public ServerSocket() throws IOException 建立非繫結伺服器套接字。 |
常用方法:
序號 | 方法描述 |
---|---|
1 | public int getLocalPort() 返回此套接字在其上偵聽的埠。 |
2 | public Socket accept() throws IOException 偵聽並接受到此套接字的連線。 |
3 | public void setSoTimeout(int timeout) 通過指定超時值啟用/禁用 SO_TIMEOUT,以毫秒為單位。 |
4 | public void bind(SocketAddress host, int backlog) 將 ServerSocket 繫結到特定地址(IP 地址和埠號)。 |
10.3 Socket類的方法
構造方法:
序號 | 方法描述 |
---|---|
1 | public Socket(String host, int port) throws UnknownHostException, IOException. 建立一個流套接字並將其連線到指定主機上的指定埠號。 |
2 | public Socket(InetAddress host, int port) throws IOException 建立一個流套接字並將其連線到指定 IP 地址的指定埠號。 |
3 | public Socket(String host, int port, InetAddress localAddress, int localPort) throws IOException. 建立一個套接字並將其連線到指定遠端主機上的指定遠端埠。 |
4 | public Socket(InetAddress host, int port, InetAddress localAddress, int localPort) throws IOException. 建立一個套接字並將其連線到指定遠端地址上的指定遠端埠。 |
5 | public Socket() 通過系統預設型別的 SocketImpl 建立未連線套接字 |
常用方法:
序號 | 方法描述 |
---|---|
1 | public Socket(String host, int port) throws UnknownHostException, IOException. 建立一個流套接字並將其連線到指定主機上的指定埠號。 |
2 | public Socket(InetAddress host, int port) throws IOException 建立一個流套接字並將其連線到指定 IP 地址的指定埠號。 |
3 | public Socket(String host, int port, InetAddress localAddress, int localPort) throws IOException. 建立一個套接字並將其連線到指定遠端主機上的指定遠端埠。 |
4 | public Socket(InetAddress host, int port, InetAddress localAddress, int localPort) throws IOException. 建立一個套接字並將其連線到指定遠端地址上的指定遠端埠。 |
5 | public Socket() 通過系統預設型別的 SocketImpl 建立未連線套接字 |
當 Socket 構造方法返回,並沒有簡單的例項化了一個 Socket 物件,它實際上會嘗試連線到指定的伺服器和埠。
下面列出了一些感興趣的方法,注意客戶端和伺服器端都有一個 Socket 物件,所以無論客戶端還是服務端都能夠呼叫這些方法。
序號 | 方法描述 |
---|---|
1 | public void connect(SocketAddress host, int timeout) throws IOException 將此套接字連線到伺服器,並指定一個超時值。 |
2 | public InetAddress getInetAddress() 返回套接字連線的地址。 |
3 | public int getPort() 返回此套接字連線到的遠端埠。 |
4 | public int getLocalPort() 返回此套接字繫結到的本地埠。 |
5 | public SocketAddress getRemoteSocketAddress() 返回此套接字連線的端點的地址,如果未連線則返回 null。 |
6 | public InputStream getInputStream() throws IOException 返回此套接字的輸入流。 |
7 | public OutputStream getOutputStream() throws IOException 返回此套接字的輸出流。 |
8 | public void close() throws IOException 關閉此套接字。 |
10.4 InetAddress類的方法
序號 | 方法描述 |
---|---|
1 | static InetAddress getByAddress(byte[] addr) 在給定原始 IP 地址的情況下,返回 InetAddress 物件。 |
2 | static InetAddress getByAddress(String host, byte[] addr) 根據提供的主機名和 IP 地址建立 InetAddress。 |
3 | static InetAddress getByName(String host) 在給定主機名的情況下確定主機的 IP 地址。 |
4 | String getHostAddress() 返回 IP 地址字串(以文字表現形式)。 |
5 | String getHostName() 獲取此 IP 地址的主機名。 |
6 | static InetAddress getLocalHost() 返回本地主機。 |
7 | String toString() 將此 IP 地址轉換為 String。 |
11. Java多執行緒程式設計
Java 給多執行緒程式設計提供了內建的支援。 一條執行緒指的是程序中一個單一順序的控制流,一個程序中可以併發多個執行緒,每條執行緒並行執行不同的任務。
多執行緒是多工的一種特別的形式,但多執行緒使用了更小的資源開銷。
這裡定義和執行緒相關的另一個術語 - 程序:一個程序包括由作業系統分配的記憶體空間,包含一個或多個執行緒。一個執行緒不能獨立的存在,它必須是程序的一部分。一個程序一直執行,直到所有的非守護執行緒都結束執行後才能結束。
多執行緒能滿足程式設計師編寫高效率的程式來達到充分利用 CPU 的目的。
11.1 執行緒的生命週期
-
新建狀態:
使用 new 關鍵字和 Thread 類或其子類建立一個執行緒物件後,該執行緒物件就處於新建狀態。它保持這個狀態直到程式 start() 這個執行緒。
-
就緒狀態:
當執行緒物件呼叫了start()方法之後,該執行緒就進入就緒狀態。就緒狀態的執行緒處於就緒佇列中,要等待JVM裡執行緒排程器的排程。
-
執行狀態:
如果就緒狀態的執行緒獲取 CPU 資源,就可以執行 run(),此時執行緒便處於執行狀態。處於執行狀態的執行緒最為複雜,它可以變為阻塞狀態、就緒狀態和死亡狀態。
-
阻塞狀態:
如果一個執行緒執行了sleep(睡眠)、suspend(掛起)等方法,失去所佔用資源之後,該執行緒就從執行狀態進入阻塞狀態。在睡眠時間已到或獲得裝置資源後可以重新進入就緒狀態。可以分為三種:
- 等待阻塞:執行狀態中的執行緒執行 wait() 方法,使執行緒進入到等待阻塞狀態。
- 同步阻塞:執行緒在獲取 synchronized 同步鎖失敗(因為同步鎖被其他執行緒佔用)。
- 其他阻塞:通過呼叫執行緒的 sleep() 或 join() 發出了 I/O 請求時,執行緒就會進入到阻塞狀態。當sleep() 狀態超時,join() 等待執行緒終止或超時,或者 I/O 處理完畢,執行緒重新轉入就緒狀態。
-
死亡狀態:
一個執行狀態的執行緒完成任務或者其他終止條件發生時,該執行緒就切換到終止狀態。
11.2 執行緒的優先順序
每一個 Java 執行緒都有一個優先順序,這樣有助於作業系統確定執行緒的排程順序。
Java 執行緒的優先順序是一個整數,其取值範圍是 1 (Thread.MIN_PRIORITY ) - 10 (Thread.MAX_PRIORITY )。
預設情況下,每一個執行緒都會分配一個優先順序 NORM_PRIORITY(5)。
具有較高優先順序的執行緒對程式更重要,並且應該在低優先順序的執行緒之前分配處理器資源。但是,執行緒優先順序不能保證執行緒執行的順序,而且非常依賴於平臺。
11.3 建立執行緒的方法
- 通過實現 Runnable 介面;
- 通過繼承 Thread 類本身;
- 通過 Callable 和 Future 建立執行緒。
Thread的常用方法
序號 | 方法描述 |
---|---|
1 | public void start() 使該執行緒開始執行;Java 虛擬機器呼叫該執行緒的 run 方法。 |
2 | public void run() 如果該執行緒是使用獨立的 Runnable 執行物件構造的,則呼叫該 Runnable 物件的 run 方法;否則,該方法不執行任何操作並返回。 |
3 | public final void setName(String name) 改變執行緒名稱,使之與引數 name 相同。 |
4 | public final void setPriority(int priority) 更改執行緒的優先順序。 |
5 | public final void setDaemon(boolean on) 將該執行緒標記為守護執行緒或使用者執行緒。 |
6 | public final void join(long millisec) 等待該執行緒終止的時間最長為 millis 毫秒。 |
7 | public void interrupt() 中斷執行緒。 |
8 | public final boolean isAlive() 測試執行緒是否處於活動狀態。 |
序號 | 方法描述 |
---|---|
1 | public static void yield() 暫停當前正在執行的執行緒物件,並執行其他執行緒。 |
2 | public static void sleep(long millisec) 在指定的毫秒數內讓當前正在執行的執行緒休眠(暫停執行),此操作受到系統計時器和排程程式精度和準確性的影響。 |
3 | public static boolean holdsLock(Object x) 當且僅當當前執行緒在指定的物件上保持監視器鎖時,才返回 true。 |
4 | public static Thread currentThread() 返回對當前正在執行的執行緒物件的引用。 |
5 | public static void dumpStack() 將當前執行緒的堆疊跟蹤列印至標準錯誤流。 |
執行緒的主要概念
- 執行緒同步
- 執行緒間通訊
- 執行緒死鎖
- 執行緒控制:掛起、停止和恢復
12. Lambda表示式
lambda 表示式的語法格式如下:
(parameters) -> expression 或 (parameters) ->{ statements; }
以下是lambda表示式的重要特徵:
- 可選型別宣告:不需要宣告引數型別,編譯器可以統一識別引數值。
- 可選的引數圓括號:一個引數無需定義圓括號,但多個引數需要定義圓括號。
- 可選的大括號:如果主體包含了一個語句,就不需要使用大括號。
- 可選的返回關鍵字:如果主體只有一個表示式返回值則編譯器會自動返回值,大括號需要指定明表示式返回了一個數值。
使用 Lambda 表示式需要注意以下兩點:
- Lambda 表示式主要用來定義行內執行的方法型別介面,例如,一個簡單方法介面。在上面例子中,我們使用各種型別的Lambda表示式來定義MathOperation介面的方法。然後我們定義了sayMessage的執行。
- Lambda 表示式免去了使用匿名方法的麻煩,並且給予Java簡單但是強大的函式化的程式設計能力。
變數作用域
lambda 表示式只能引用標記了 final 的外層區域性變數,這就是說不能在 lambda 內部修改定義在域外的區域性變數,否則會編譯錯誤。
lambda 表示式的區域性變數可以不用宣告為 final,但是必須不可被後面的程式碼修改(即隱性的具有 final 的語義)
在 Lambda 表示式當中不允許宣告一個與區域性變數同名的引數或者區域性變數。
13. 方法引用
方法引用通過方法的名字來指向一個方法。
方法引用可以使語言的構造更緊湊簡潔,減少冗餘程式碼。
方法引用使用一對冒號 :: 。
14. 函式式介面
函式式介面(Functional Interface)就是一個有且僅有一個抽象方法,但是可以有多個非抽象方法的介面。
函式式介面可以被隱式轉換為 lambda 表示式。
Lambda 表示式和方法引用(實際上也可認為是Lambda表示式)上。
如定義了一個函式式介面如下:
@FunctionalInterface
interface GreetingService
{
void sayMessage(String message);
}
那麼就可以使用Lambda表示式來表示該介面的一個實現(注:JAVA 8 之前一般是用匿名類實現的):
GreetingService greetService1 = message -> System.out.println("Hello " + message);
Java8 預設方法
Java 8 新增了介面的預設方法。
簡單說,預設方法就是介面可以有實現方法,而且不需要實現類去實現其方法。
我們只需在方法名前面加個 default 關鍵字即可實現預設方法。
為什麼要有這個特性?
首先,之前的介面是個雙刃劍,好處是面向抽象而不是面向具體程式設計,缺陷是,當需要修改介面時候,需要修改全部實現該介面的類,目前的 java 8 之前的集合框架沒有 foreach 方法,通常能想到的解決辦法是在JDK裡給相關的介面新增新的方法及實現。然而,對於已經發布的版本,是沒法在給介面新增新方法的同時不影響已有的實現。所以引進的預設方法。他們的目的是為了解決介面的修改與現有的實現不相容的問題。
Java8 Stream
Java 8 API添加了一個新的抽象稱為流Stream,可以讓你以一種宣告的方式處理資料。
Stream 使用一種類似用 SQL 語句從資料庫查詢資料的直觀方式來提供一種對 Java 集合運算和表達的高階抽象。
Stream API可以極大提高Java程式設計師的生產力,讓程式設計師寫出高效率、乾淨、簡潔的程式碼。
這種風格將要處理的元素集合看作一種流, 流在管道中傳輸, 並且可以在管道的節點上進行處理, 比如篩選, 排序,聚合等。
元素流在管道中經過中間操作(intermediate operation)的處理,最後由最終操作(terminal operation)得到前面處理的結果。
什麼是Stream?
Stream(流)是一個來自資料來源的元素佇列並支援聚合操作
- 元素是特定型別的物件,形成一個佇列。 Java中的Stream並不會儲存元素,而是按需計算。
- 資料來源 流的來源。 可以是集合,陣列,I/O channel, 產生器generator 等。
- 聚合操作 類似SQL語句一樣的操作, 比如filter, map, reduce, find, match, sorted等。
和以前的Collection操作不同, Stream操作還有兩個基礎的特徵:
- Pipelining: 中間操作都會返回流物件本身。 這樣多個操作可以串聯成一個管道, 如同流式風格(fluent style)。 這樣做可以對操作進行優化, 比如延遲執行(laziness)和短路( short-circuiting)。
- 內部迭代: 以前對集合遍歷都是通過Iterator或者For-Each的方式, 顯式的在集合外部進行迭代, 這叫做外部迭代。 Stream提供了內部迭代的方式, 通過訪問者模式(Visitor)實現。
生成流
在 Java 8 中, 集合介面有兩個方法來生成流:
- stream() − 為集合建立序列流。
- parallelStream() − 為集合建立並行流。
Java8 Optional類
Optional 類是一個可以為null的容器物件。如果值存在則isPresent()方法會返回true,呼叫get()方法會返回該物件。
Optional 是個容器:它可以儲存型別T的值,或者僅僅儲存null。Optional提供很多有用的方法,這樣我們就不用顯式進行空值檢測。
Optional 類的引入很好的解決空指標異常。
序號 | 方法 & 描述 |
---|---|
1 |
static |
2 | boolean equals(Object obj)判斷其他物件是否等於 Optional。 |
3 |
Optional |
4 | Optional flatMap(Function<? super T,Optional> mapper)如果值存在,返回基於Optional包含的對映方法的值,否則返回一個空的Optional |
5 | T get()如果在這個Optional中包含這個值,返回值,否則丟擲異常:NoSuchElementException |
6 | int hashCode()返回存在值的雜湊碼,如果值不存在 返回 0。 |
7 | void ifPresent(Consumer<? super T> consumer)如果值存在則使用該值呼叫 consumer , 否則不做任何事情。 |
8 | boolean isPresent()如果值存在則方法會返回true,否則返回 false。 |
9 | Optional map(Function<? super T,? extends U> mapper)如果有值,則對其執行呼叫對映函式得到返回值。如果返回值不為 null,則建立包含對映返回值的Optional作為map方法返回值,否則返回空Optional。 |
10 |
static |
11 |
static |
12 | T orElse(T other)如果存在該值,返回值, 否則返回 other。 |
13 | T orElseGet(Supplier<? extends T> other)如果存在該值,返回值, 否則觸發 other,並返回 other 呼叫的結果。 |
14 |
|
15 | String toString()返回一個Optional的非空字串,用來除錯 |
Java8 日期時間API
Java 8通過釋出新的Date-Time API (JSR 310)來進一步加強對日期與時間的處理。
在舊版的 Java 中,日期時間 API 存在諸多問題,其中有:
- 非執行緒安全 − java.util.Date 是非執行緒安全的,所有的日期類都是可變的,這是Java日期類最大的問題之一。
- 設計很差 − Java的日期/時間類的定義並不一致,在java.util和java.sql的包中都有日期類,此外用於格式化和解析的類在java.text包中定義。java.util.Date同時包含日期和時間,而java.sql.Date僅包含日期,將其納入java.sql包並不合理。另外這兩個類都有相同的名字,這本身就是一個非常糟糕的設計。
- 時區處理麻煩 − 日期類並不提供國際化,沒有時區支援,因此Java引入了java.util.Calendar和java.util.TimeZone類,但他們同樣存在上述所有的問題。
Java 8 在 java.time 包下提供了很多新的 API。以下為兩個比較重要的 API:
- Local(本地) − 簡化了日期時間的處理,沒有時區的問題。
- Zoned(時區) − 通過制定的時區處理日期時間。
新的java.time包涵蓋了所有處理日期,時間,日期/時間,時區,時刻(instants),過程(during)與時鐘(clock)的操作。