什么是静态工厂方法
返回一个类的实例的静态方法
优势
- 不要用多态的方法创建构造器,可以选择不同命名的静态工厂方法来创建实例
- 在类里可以预先构建好实例或者将实例缓存起来,然后重复返回相同对象,不用每次都new一个对象
- 可以返回子类
多个构造器解决方案
假如有2个必选参数和4个可选参数,那么构造函数至少要有5种,而且,每一次选择构造函数的时候还需要注意参数顺序,看api的时候还要点进去看
方案1:一个空的构造函数,其余参数用set方法来进行赋值,但这个有个不好的地方是,不能保证一个事务
方案2:builder模式,不直接生成对象,先生成一个builder对象,然后再将builder对象生成目标对象,具体例子如下:
public class NutritionFacts {
private final int servingSize; //require
private final int servings; //require
private final int calories; //optional
private final int fat; //optional
private final int sodium; //optional
private final int carbohydrate; //optional
public static class Builder {
//对象的必选参数
private final int servingSize;
private final int servings;
//对象的可选参数的缺省值初始化
private int calories = 0;
private int fat = 0;
private int carbohydrate = 0;
private int sodium = 0;
//只用少数的必选参数作为构造器的函数参数
public Builder(int servingSize,int servings) {
this.servingSize = servingSize;
this.servings = servings;
}
public Builder calories(int val) {
calories = val;
return this;
}
public Builder fat(int val) {
fat = val;
return this;
}
public Builder carbohydrate(int val) {
carbohydrate = val;
return this;
}
public Builder sodium(int val) {
sodium = val;
return this;
}
public NutritionFacts build() {
return new NutritionFacts(this);
}
}
private NutritionFacts(Builder builder) {
servingSize = builder.servingSize;
servings = builder.servings;
calories = builder.calories;
fat = builder.fat;
sodium = builder.sodium;
carbohydrate = builder.carbohydrate;
}
}
//使用方式
public static void main(String[] args) {
NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8).calories(100)
.sodium(35).carbohydrate(27).build();
System.out.println(cocaCola);
}
单例模式
public class Elvis {
public static final Elvis INSTANCE = new Elvis();
private Elivs() { ... }
public static Elvis getInstance() {
return INSTANCE;
}
public void leaveTheBuilding() { ... }
}
避免创建不必要的对象
String s = new String("aaa");
String s = "aaa";
比较这两行代码,上面的语句每次执行的时候都会创建一个新的 String 实例,但是这些创建对象的动作全都是不必要的。而下面的语句只用了一个 String 实例,而不是每次执行的时候都创建一个新的实例。由于 String 被实现为不可变对象,JVM 底层将其实现为常量池,所有值等于"aaa"的对象实例共享同一对象地址,而且还可以保证,对于所有在同一 JVM 中运行的代码,只要他们包含相同的字符串字面常量,该对象就会被重用。
拓展:string、stringBuilder、stringBuffer的区别
String 字符串常量
(每次对 String 类型进行改变的时候其实都等同于生成了一个新的 String 对象,然后将指针指向新的 String 对象,所以经常改变内容的字符串最好不要用 String)
StringBuffer 字符串变量(线程安全)对方法加了同步锁
public synchronized StringBuffer reverse() {
super.reverse();
return this;
}
public int indexOf(String str) {
return indexOf(str, 0); //存在 public synchronized int indexOf(String str, int fromIndex) 方法
}
*** synchronized的含义:
每一个类对象都对应一把锁,当某个线程A调用类对象O中的synchronized方法M时,必须获得对象O的锁才能够执行M方法,否则线程A阻塞。一旦线程A开始执行M方法,将独占对象O的锁。使得其它需要调用O对象的M方法的线程阻塞。只有线程A执行完毕,释放锁后。那些阻塞线程才有机会重新调用M方法。这就是解决线程同步问题的锁机制。 ***
StringBuilder 字符串变量(非线程安全)
总结
1.如果要操作少量的数据 <=> String
2.单线程操作字符串缓冲区 下操作大量数据 <=> StringBuilder
3.多线程操作字符串缓冲区 下操作大量数据 <=> StringBuffer
当一个类里面一些对象变量不会改变或者只需要初始化一遍时,可以进行如下操作,将变量初始化为final类型,并在static方法里面完成赋值,重用那些已知不会被修改的可变对象
public class Person {
private static final Date BOOM_START;
private static final Date BOOM_END;
static {
Calender c = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
c.set(1946,Calendar.JANUARY,1,0,0,0);
BOOM_START = c.getTime();
c.set(1965,Calendar.JANUARY,1,0,0,0);
BOOM_END = c.getTime();
}
public boolean isBabyBoomer() {
return birthDate.compareTo(BOOM_START) >= 0 && birthDate.compareTo(BOOM_END) < 0;
}
}