Java原始碼分析——java.util工具包解析(五)——UUID、Base64、內建觀察者模式Observer介面、EventListener、RandomAccess
UUID
關於UUID,我們需要知道它最重要的一點,就是它會生成全地球唯一的一個id,它可以作為資料庫的主鍵存在,標識各個元組。 UUID保證對在同一時空中的所有機器都是唯一的,利用機器的當前日期和時間、時鐘序列、全域性唯一的IEEE機器識別號來生成唯一的一個id。其用法如下:
//得到一個UUID
System.out.println(UUID.randomUUID());
Base64
Base64類是將不是ASCII碼的字串轉換為ASCII碼格式的,可以做到簡單的將密碼的明文轉變為非明文,但不能做到保密,一般用在網路傳輸上,需要ASCII碼的地方,用法如下:
//編碼
String asB64 = Base64.getEncoder().encodeToString("蕾姆".getBytes("utf-8"));
System.out.println(asB64); // 輸出為: c29tZSBzdHJpbmc=
// 解碼
byte[] asBytes = Base64.getDecoder().decode("6JW+5aeG");
//輸出:6JW+5aeG
//蕾姆
Observer
Observer類是Java內建的觀察者模式,用一張圖來說明下觀察者模式:
對觀察者模式舉個例子的話,就像微信的推送訊息,每個微信的使用者都是觀察者,公眾號就是一個被觀察者,當公眾號有訊息更新後,會同步通知每個觀察者說我要進行訊息的推送了,然後將訊息推送給每個觀察者。可以理解為廣播模式,被觀察者通過廣播通知各個觀察者。在Java中,定義了一個Observer介面,定義了訊息的更新操作:
public interface Observer {
void update(Observable o, Object arg);
}
接著是被觀察者:
public class Observable {
//changed表示是否需要更新
private boolean changed = false;
//存貯觀察者
private Vector<Observer> obs;
public Observable() {
obs = new Vector<>();
}
//增加一個觀察者
public synchronized void addObserver(Observer o) {
if (o == null)
throw new NullPointerException();
if (!obs.contains(o)) {
obs.addElement(o);
}
}
//刪除一個觀察者
public synchronized void deleteObserver(Observer o) {
obs.removeElement(o);
}
//通知所有觀察者
public void notifyObservers() {
notifyObservers(null);
}
//通知所有的觀察者
public void notifyObservers(Object arg) {
Object[] arrLocal;
synchronized (this) {
if (!changed)
return;
arrLocal = obs.toArray();
clearChanged();
}
for (int i = arrLocal.length-1; i>=0; i--)
((Observer)arrLocal[i]).update(this, arg);
}
//刪除所有的觀察者
public synchronized void deleteObservers() {
obs.removeAllElements();
}
//表明可以進行更新
protected synchronized void setChanged() {
changed = true;
}
//更新完設定不可更新
protected synchronized void clearChanged() {
changed = false;
}
//返回是否更新
public synchronized boolean hasChanged() {
return changed;
}
//觀察者的數量
public synchronized int countObservers() {
return obs.size();
}
}
從原始碼中看出,該類是個同步類,執行緒安全,每次更新完都會設定不可更新。如何用這兩個類實現一個觀察者模式呢?首先,先定義觀察者類實現Observer介面,我在這定義了DateUpdateObserver資料更新以及ViewUpdateObserver檢視更新:
public class ViewUpdateObserver implements Observer {
@Override
public void update(Observable o, Object arg) {
System.out.println("檢視改變了");
}
}
public class DateUpdateObserver implements Observer {
@Override
public void update(Observable o, Object arg) {
System.out.println("資料改變了");
}
}
接著實現被觀察者繼承Observable 類,因為設定可以更新的方法是保護方法,所以必須繼承Observable 類來設定可以更新的值,,我這裡為了簡便直接在構造器裡面設定了:
public class ShowUpdate extends Observable {
public ShowUpdate(){
setChanged();
}
}
測試:
Observable observable=new ShowUpdate();
observable.addObserver(new DateUpdateObserver());
observable.addObserver(new ViewUpdateObserver());
observable.notifyObservers();
//輸出:檢視改變了 資料改變了
EventListener、RandomAccess
關於這兩個介面,閱讀過它兩原始碼的同學都知道,它們都是空的介面,沒有定義任何東西,是起標記用的:
public interface RandomAccess {
}
public interface EventListener {
}
先說RandomAccess介面,該介面是一個標記為隨機訪問的介面,是什麼意思呢?RandomAccess介面被ArrayList、Vector實現過,是讓它判斷是否是陣列型別的容器的,因為只有陣列型別的容器可以用下標來實現隨機的訪問。這一點用到了Collections容器的選擇查詢對應值的索引上:
public static <T>
int binarySearch(List<? extends Comparable<? super T>> list, T key) {
if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD)
return Collections.indexedBinarySearch(list, key);
else
return Collections.iteratorBinarySearch(list, key);
}
從原始碼中看出,當容器實現了RandomAccess 介面時,用索引值查詢的方式來進行二分搜尋,否則就用迭代器來進行,因為陣列型別的容器,即ArrayList的索引值查詢比迭代器查詢快很多。
而EventListener介面可以用來實現自定義的監聽器,試想一個網路請求資料的非同步場景,當你向伺服器請求時,假如這個請求是非同步的請求,伺服器並不會立馬返回,而是需要等待一會兒,這時候你就要寫個監聽來監聽它的資料的返回,我們可以利用這個介面來實現:
public interface NetworkListener extends EventListener {
void success();
void fail();
}
public class NetworkListenerIml implements NetworkListener{
@Override
public void success() {
System.out.println("成功了");
}
@Override
public void fail() {
System.out.println("失敗了");
}
}
public class NetworkRequest {
private NetworkListener eventListener;
public void addEventListener(NetworkListener eventListener){
this.eventListener=eventListener;
}
public void request(){
eventListener.success();
}
}
NetworkRequest networkRequest=new NetworkRequest();
networkRequest.addEventListener(new NetworkListenerIml());
networkRequest.request();
這樣就把程式碼間的模組清晰化了,是程式碼的複用以及可拓展性大大的提高了。