用C++的random庫生成更好的隨機數
阿新 • • 發佈:2021-02-05
享元模式
1 概述
定義:
運用共享技術來有效地支援大量細粒度物件的複用。它通過共享已經存在的物件來大幅度減少需要建立的物件數量、避免大量相似物件的開銷,從而提高系統資源的利用率。
2 結構
享元(Flyweight )模式中存在以下兩種狀態:
- 內部狀態,即不會隨著環境的改變而改變的可共享部分。
- 外部狀態,指隨環境改變而改變的不可以共享的部分。享元模式的實現要領就是區分應用中的這兩種狀態,並將外部狀態外部化。
享元模式的主要有以下角色:
- 抽象享元角色(Flyweight):通常是一個介面或抽象類,在抽象享元類中聲明瞭具體享元類公共的方法,這些方法可以向外界提供享元物件的內部資料(內部狀態),同時也可以通過這些方法來設定外部資料(外部狀態)。
- 具體享元(Concrete Flyweight)角色 :它實現了抽象享元類,稱為享元物件;在具體享元類中為內部狀態提供了儲存空間。通常我們可以結合單例模式來設計具體享元類,為每一個具體享元類提供唯一的享元物件。
- 非享元(Unsharable Flyweight)角色 :並不是所有的抽象享元類的子類都需要被共享,不能被共享的子類可設計為非共享具體享元類;當需要一個非共享具體享元類的物件時可以直接通過例項化建立。
- 享元工廠(Flyweight Factory)角色 :負責建立和管理享元角色。當客戶物件請求一個享元物件時,享元工廠檢査系統中是否存在符合要求的享元物件,如果存在則提供給客戶;如果不存在的話,則建立一個新的享元物件。
程式碼如下:
/** * @author WGR * @create 2021/1/13 -- 21:11 */ public abstract class Animal { public abstract String getName(); public void getInfo(Integer age){ System.out.println("age:"+age+",name:"+getName()); } } /** * @author WGR * @create 2021/1/13 -- 21:18 */ public class Cat extends Animal { @Override public String getName() { return "貓"; } } /** * @author WGR * @create 2021/1/13 -- 21:20 */ public class Dog extends Animal { @Override public String getName() { return "狗"; } } import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * @author WGR * @create 2021/1/13 -- 21:25 */ public class AnimalFactory { private static Map<String,Animal> animals = new ConcurrentHashMap<>(); public static Animal getAnimal(String key){ Animal animal; if(!animals.containsKey(key)){ if("貓".equals(key)){ animal = new Cat(); }else{ animal = new Dog(); } animals.put(key, animal); } return animals.get(key); }; }
測試程式碼:
/**
* @author WGR
* @create 2021/1/13 -- 21:34
*/
public class Test {
public static void main(String[] args) {
Animal cat1 = AnimalFactory.getAnimal("貓");
Animal cat2 = AnimalFactory.getAnimal("貓");
Animal dog = AnimalFactory.getAnimal("狗");
cat1.getInfo(13);
cat2.getInfo(12);
dog.getInfo(11);
System.out.println(cat1 == cat2);
}
}
3 優缺點和使用場景
1,優點
- 極大減少記憶體中相似或相同物件數量,節約系統資源,提供系統性能
- 享元模式中的外部狀態相對獨立,且不影響內部狀態
2,缺點:
為了使物件可以共享,需要將享元物件的部分狀態外部化,分離內部狀態和外部狀態,使程式邏輯複雜
3,使用場景:
- 一個系統有大量相同或者相似的物件,造成記憶體的大量耗費。
- 物件的大部分狀態都可以外部化,可以將這些外部狀態傳入物件中。
- 在使用享元模式時需要維護一個儲存享元物件的享元池,而這需要耗費一定的系統資源,因此,應當在需要多次重複使用享元物件時才值得使用享元模式。
4 JDK原始碼解析
Integer類使用了享元模式。我們先看下面的例子:
/**
* @author WGR
* @create 2021/1/13 -- 22:00
*/
public class Demo {
public static void main(String[] args) {
Integer i1 = 127;
Integer i2 = 127;
System.out.println("i1和i2物件是否是同一個物件?" + (i1 == i2));
Integer i3 = 128;
Integer i4 = 128;
System.out.println("i3和i4物件是否是同一個物件?" + (i3 == i4));
}
}
為什麼第一個輸出語句輸出的是true,第二個輸出語句輸出的是false?通過反編譯軟體進行反編譯,程式碼如下:
public class Demo {
public static void main(String[] args) {
Integer i1 = Integer.valueOf((int)127);
Integer i2 Integer.valueOf((int)127);
System.out.println((String)new StringBuilder().append((String)"i1\u548ci2\u5bf9\u8c61\u662f\u5426\u662f\u540c\u4e00\u4e2a\u5bf9\u8c61\uff1f").append((boolean)(i1 == i2)).toString());
Integer i3 = Integer.valueOf((int)128);
Integer i4 = Integer.valueOf((int)128);
System.out.println((String)new StringBuilder().append((String)"i3\u548ci4\u5bf9\u8c61\u662f\u5426\u662f\u540c\u4e00\u4e2a\u5bf9\u8c61\uff1f").append((boolean)(i3 == i4)).toString());
}
}
上面程式碼可以看到,直接給Integer型別的變數賦值基本資料型別資料的操作底層使用的是 valueOf()
,所以只需要看該方法即可
public final class Integer extends Number implements Comparable<Integer> {
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
}
可以看到 Integer
預設先建立並快取 -128 ~ 127
之間數的 Integer
物件,當呼叫 valueOf
時如果引數在 -128 ~ 127
之間則計算下標並從快取中返回,否則建立一個新的 Integer
物件。