Java构造器与构建器的使用
我们在平常类的构建过程中,可能会面临很多问题,可扩张性、安全性等等。想象一下,这样一个场景,我们现在要创建一个类,其中有6个属性,其中又有4个属性的值是不太确定的(可能某个对象就不需要其中的某个值),这时我们怎么创建这个类呢?以下是几种方法:
使用普通构造器
1 public class Test { 2 private int servingSize; 3 private int servings; 4 private int calories; 5 private int fat; 6 private int sodium; 7 private int carbohydrate; 8 9 public Test(int servingSize, int servings) { 10 this.servingSize = servingSize; 11 this.servings = servings; 12 this.calories = 0; 13 } 14 15 public Test(int servingSize, int servings, int calories) { 16 this.servingSize = servingSize; 17 this.servings = servings; 18 this.calories = calories; 19 this.fat = 0; 20 } 21 22 public Test(int servingSize, int servings, int calories, int fat) { 23 this.servingSize = servingSize; 24 this.servings = servings; 25 this.calories = calories; 26 this.fat = fat; 27 this.sodium = 0; 28 } 29 30 public Test(int servingSize, int servings, int calories, int fat, int sodium) { 31 this.servingSize = servingSize; 32 this.servings = servings; 33 this.calories = calories; 34 this.fat = fat; 35 this.sodium = sodium; 36 this.carbohydrate = 0; 37 } 38 39 public Test(int servingSize, int servings, int calories, int fat, int sodium, int carbohydrate) { 40 this.servingSize = servingSize; 41 this.servings = servings; 42 this.calories = calories; 43 this.fat = fat; 44 this.sodium = sodium; 45 this.carbohydrate = carbohydrate; 46 } 47 48 }
我们完成该类构建后,接下来就是调用的过程:
1 public static void main(String[] args) { 2 Test test = new Test(1,2,3,0,5,6); 3 }
如上所示,在实例化对象时,我们需要传入相应的值,这时发现:
- 第四个参数不是我们需要的,但是还不得不给他传递一个值
- 我们在传值时,很容易出错,某两个参数值互换了位置,这在工作时是不好发现的,但是程序会报错
所以上面的方式在涉及到参数比较多,而且参数值不太确定是否需要时,这种方法会给我们的编码以及后期维护带来很大的困扰,我们再改进一下。
JavaBeans模式
1 public class Test { 2 private int servingSize; 3 private int servings; 4 private int calories; 5 private int fat; 6 private int sodium; 7 private int carbohydrate; 8 9 public Test() { 10 11 } 12 13 public void setServingSize(int servingSize) { 14 this.servingSize = servingSize; 15 } 16 17 public void setServings(int servings) { 18 this.servings = servings; 19 } 20 21 public void setCalories(int calories) { 22 this.calories = calories; 23 } 24 25 public void setFat(int fat) { 26 this.fat = fat; 27 } 28 29 public void setSodium(int sodium) { 30 this.sodium = sodium; 31 } 32 33 public void setCarbohydrate(int carbohydrate) { 34 this.carbohydrate = carbohydrate; 35 } 36 37 38 }
如上,我们先创建一个无参构造方法(可以不写出来,会默认创建),接下来就是利用setter方法给属性赋值
1 public static void main(String[] args) { 2 Test test = new Test(); 3 test.setServingSize(1); 4 test.setCalories(2); 5 test.setCalories(3); 6 test.setSodium(5); 7 test.setCarbohydrate(6); 8 }
如上,我们需要的对象不需要fat属性,我们就不用给其赋值,这中方法有几个好处:
- 客户端调用简单,也就是实例化对象的过程十分简单,并且不会出现把值传递出错的风险
- 别人能够很好的使用且理解简单
但是我们知道,javaBeans有个缺点:
- 线程不安全,因为对象的创建分在了好几步的过程中,不能保证对象状态的一致性
我们可以确保线程的安全,这就需要我们额外的精力(我们可以在对象构造完成,并且不允许在冻结之前使用时,手动冻结,实际中很少使用,编译器无法确定我们是否调用的freeze方法
)。因此这种方法也不够理想,还有什么继续改进的地方吗?
构建器(建造者模式的一种形式)
1 public class Test { 2 private int servingSize; 3 private int servings; 4 private int calories; 5 private int fat; 6 private int sodium; 7 private int carbohydrate; 8 9 public static class Builder { 10 private int servingSize; 11 private int servings; 12 private int calories; 13 private int fat; 14 private int sodium; 15 private int carbohydrate; 16 17 public Builder (int servingSize, int servings) { 18 this.servingSize = servingSize; 19 this.servings = servings; 20 } 21 22 public Builder calories(int calories) { 23 calories = calories; 24 return this; 25 } 26 27 public Builder fat(int fat) { 28 fat = fat; 29 return this; 30 } 31 32 public Builder sodium(int sodium) { 33 sodium = sodium; 34 return this; 35 } 36 37 public Builder carbohydrate(int carbohydrate) { 38 carbohydrate = carbohydrate; 39 return this; 40 } 41 42 public Test builder () { 43 return new Test(this); 44 } 45 } 46 47 private Test(Builder builder) { 48 servingSize = builder.servingSize; 49 servings = builder.servings; 50 calories = builder.calories; 51 fat = builder.fat; 52 sodium = builder.sodium; 53 carbohydrate = builder.carbohydrate; 54 } 55 }
调用方式:
1 public static void main(String[] args) { 2 Test test = new Builder(1,2).calories(3).fat(4).sodium(5).carbohydrate(6).builder(); 3}
这样就保证了对象在不变的情况下,简单明了地实现了对象实例化(至于代码中的内部类,我们以后细说)。