建造者模式
建造者模式是一種對象創建模式,用於組合對象的創建。當一個對象由多個對象組合而成,並且組合可能發生較大的變化時,可以採用建造者模式將子對象的構造和子對象的組合封裝起來。以下是快餐店組裝食物的一個例子。
驅動類:Main.java
public class Main {
public static void main(String[] args) {
Meal chickenMeal = MealBuilder.getChickenMeal();
System.out.println(chickenMeal); // Meal: Chicken Burger + Coke, Price: 14.0
Meal beefMeal = MealBuilder.getBeefMeal();
System.out.println(beefMeal); // Meal: Beef Burger + Orange Juice, Price: 20.0
}
}
主食基類:Burger.java
public abstract class Burger {
public String getVegetable() {
return "lettuce";
}
public abstract String getMeat();
public abstract float getPrice();
}
飲料基類:Drink.java
public interface Drink {
public String getLiquid();
public float getPrice();
}
主食類:ChickenBurger.java
public class ChickenBurger extends Burger {
@Override
public String getMeat() {
return "Chicken";
}
@Override
public float getPrice() {
return 10.5f;
}
@Override
public String toString() {
return "Chicken Burger";
}
}
主食類:BeefBurger.java
public class BeefBurger extends Burger {
@Override
public String getMeat() {
return "Beef";
}
@Override
public float getPrice() {
return 15.5f;
}
@Override
public String toString() {
return "Beef Burger";
}
}
飲料類:Coke.java
public class Coke implements Drink {
@Override
public String getLiquid() {
return "coke";
}
@Override
public float getPrice() {
return 3.5f;
}
@Override
public String toString() {
return "Coke";
}
}
飲料類:OrangeJuice.java
public class OrangeJuice implements Drink {
@Override
public String getLiquid() {
return "orange";
}
@Override
public float getPrice() {
return 4.5f;
}
@Override
public String toString() {
return "Orange Juice";
}
}
套餐類:Meal.java
public class Meal {
private Burger burger;
private Drink drink;
public Burger getBurger() {
return burger;
}
public void setBurger(Burger burger) {
this.burger = burger;
}
public Drink getDrink() {
return drink;
}
public void setDrink(Drink drink) {
this.drink = drink;
}
public float getPrice() {
return burger.getPrice() + drink.getPrice();
}
@Override
public String toString() {
return "Meal: " + burger.toString() + " + " + drink.toString() + ", Price: " + getPrice();
}
}
套餐建造者類:MealBuilder.java
public class MealBuilder {
public static Meal getChickenMeal() {
Meal meal = new Meal();
meal.setBurger(new ChickenBurger());
meal.setDrink(new Coke());
return meal;
}
public static Meal getBeefMeal() {
Meal meal = new Meal();
meal.setBurger(new BeefBurger());
meal.setDrink(new OrangeJuice());
return meal;
}
}
StringBuilder源碼
StringBuilder
Java的StringBuider
應用了建造者模式。以下基於jdk 11.0.4
分析StringBuilder
源碼。
StringBuilder
類的簽名如下,可以看到StringBuilder
的基類是AbstractStringBuilder
:
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, Comparable<StringBuilder>, CharSequence
StringBuilder
中最重要的方法是append
方法,append
方法相當於建造者模式中建造者的對象組合方法,把boolean
, char
, int
, long
, float
, double
, String
, char[]
, CharSequence
, String
, StringBuilder
, StringBuffer
, Object
等對象組合成程序員需要的字符串。
最關鍵也是常用的方法是public StringBuilder append(String str)
,其他許多不同類型的append
方法實際是引用了字符串的append
方法。該方法的具體實現在StringBuilder
的基類AbstractStringBuilder
中。
這裏旁逸斜出一下,@HotSpotIntrinsicCandidate
註解表示這個方法在HotSpot中有更高效的基於虛擬機指令的優化實現,Java源碼中的Java實現只是功能的釋義,具體解釋見附錄。
@Override
@HotSpotIntrinsicCandidate
public StringBuilder append(String str) {
super.append(str);
return this;
}
AbstractStringBuilder
AbstractStringBuilder
類的屬性,主要是一個字節序列value
和尾字節位置標記count
:
/**
* The value is used for character storage.
*/
byte[] value;
/**
* The id of the encoding used to encode the bytes in {@code value}.
*/
byte coder;
/**
* The count is the number of characters used.
*/
int count;
AbstractStringBuilder
類的append(String)
方法,首先確認加入字符串後是否會超過當前value
的容量,如果超過容量,則開闢新的容量給value
,然後將字符串按字節放入value
:
/**
* Appends the specified string to this character sequence.
* <p>
* The characters of the {@code String} argument are appended, in
* order, increasing the length of this sequence by the length of the
* argument. If {@code str} is {@code null}, then the four
* characters {@code "null"} are appended.
* <p>
* Let <i>n</i> be the length of this character sequence just prior to
* execution of the {@code append} method. Then the character at
* index <i>k</i> in the new character sequence is equal to the character
* at index <i>k</i> in the old character sequence, if <i>k</i> is less
* than <i>n</i>; otherwise, it is equal to the character at index
* <i>k-n</i> in the argument {@code str}.
*
* @param str a string.
* @return a reference to this object.
*/
public AbstractStringBuilder append(String str) {
if (str == null) {
return appendNull();
}
int len = str.length();
ensureCapacityInternal(count + len);
putStringAt(count, str);
count += len;
return this;
}
StringBuilder與String在拼接效率上的比較
String
類的value
屬性是final
的、不可改變的,因此採用+
操作符進行字符串拼接,實際上進行了類似new
的操作,生成了新的字符串對象。
/**
* The value is used for character storage.
*
* @implNote This field is trusted by the VM, and is a subject to
* constant folding if String instance is constant. Overwriting this
* field after construction will cause problems.
*
* Additionally, it is marked with {@link Stable} to trust the contents
* of the array. No other facility in JDK provides this functionality (yet).
* {@link Stable} is safe here, because value is never null.
*/
@Stable
private final byte[] value;
而StringBuilder
類的value
屬性沒有final
關鍵字修飾,可以動態變化,因此當需要多次拼接字符串時,使用StringBuilder
省去了多次新建字符串對象的開銷,提高了效率。
而至於爲什麼String
類的value
屬性要設計成不可變的,主要是爲了安全性、線程安全性和字符串常量池的實現上的考慮。
附錄
- HotSpotIntrinsicCandidate的接口註釋
The {@code @HotSpotIntrinsicCandidate} annotation is specific to the
HotSpot Virtual Machine. It indicates that an annotated method
may be (but is not guaranteed to be) intrinsified by the HotSpot VM. A method
is intrinsified if the HotSpot VM replaces the annotated method with hand-written
assembly and/or hand-written compiler IR -- a compiler intrinsic -- to improve
performance. The {@code @HotSpotIntrinsicCandidate} annotation is internal to the
Java libraries and is therefore not supposed to have any relevance for application
code.