建造者模式与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.
发布了708 篇原创文章 · 获赞 140 · 访问量 24万+

猜你喜欢

转载自blog.csdn.net/da_kao_la/article/details/104105013