建造者模式與StringBuilder源碼

建造者模式

建造者模式是一種對象創建模式,用於組合對象的創建。當一個對象由多個對象組合而成,並且組合可能發生較大的變化時,可以採用建造者模式將子對象的構造和子對象的組合封裝起來。以下是快餐店組裝食物的一個例子。
驅動類: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.
發佈了715 篇原創文章 · 獲贊 141 · 訪問量 24萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章