建造者模式
建造者模式是一种对象创建模式,用于组合对象的创建。当一个对象由多个对象组合而成,并且组合可能发生较大的变化时,可以采用建造者模式将子对象的构造和子对象的组合封装起来。以下是快餐店组装食物的一个例子。
驱动类: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.