Num1:考虑用静态工厂方法代替构造器
对于类而言,常见的方法是提供一个公有的构造器,但其实还有一种方法叫做静态工厂方法(static factory method),它只是一个返回类的实例静态方法。
目前比较流行的规范是把静态工厂方法命名为valueOf
或者getInstance
。
valueOf
:该方法返回的实例与它的参数具有同样的值,例如:
1
| Integer a=Integer.valueOf(100);
|
从上面代码可以看出,valueOf()方法能执行类型转换操作,在本例中,把int类型的基本数据转换为Integer对象。
getInstance
:返回的实例与参数匹配,例如:
1
| Calendar cal=Calendar.getInstance(Locale.CHINA);
|
优势:
- 静态工厂方法与构造器不同的第一大优势在于,他们有名称,更有可读性。
- 静态工厂方法与构造器不同的第二大优势在于,不必每次调用它们的时候都创建一个新对象。
- 静态工厂方法与构造器不同的第三大优势在于,它们可以返回原返回类型的任何子类型的对象。
- 静态工厂方法与构造器不同的第四大优势在于,在创建参数化实例的时候,它们使代码变得更加简洁。
缺点:
- 类如果不含公有的或者受保护的构造器,就不能被子类化。
- 它们与其他的静态方法实际上没有任何区别。
Num2:遇到多个构造器参数时要考虑用构造器
一般有以下三种构造器的方式
- 重叠构造器模式
- JavaBeans模式
- Builder模式
重叠构造器模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| public class NutritionFacts { private final int servingSize; private final int servings; private final int calories; private final int fat; private final int sodium; private final int carbohydrate;
public NutritionFacts(int servingSize, int servings) { this(servingSize, servings, 0); }
public NutritionFacts(int servingSize, int servings, int calories) { this(servingSize, servings, calories, 0); }
public NutritionFacts(int servingSize, int servings, int calories, int fat) { this(servingSize, servings, calories, fat, 0); }
public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium) { this(servingSize, servings, calories, fat, sodium, 0); }
public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium, int carbohydrate) { this.servingSize = servingSize; this.servings = servings; this.calories = calories; this.fat = fat; this.sodium = sodium; this.carbohydrate = carbohydrate; }
public static void main(String[] args) { NutritionFacts cocaCola = new NutritionFacts(240, 8, 100, 0, 35, 27); } }
|
JavaBeans模式
在这种模式下,调用一个无参构造器来创建对象,然后调用setter
方法来设置每个必要的参数。如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| public class NutritionFacts { private int servingSize = -1; private int servings = -1; private int calories = 0; private int fat = 0; private int sodium = 0; private int carbohydrate = 0;
public NutritionFacts() { }
public void setServingSize(int val) { servingSize = val; }
public void setServings(int val) { servings = val; }
public void setCalories(int val) { calories = val; }
public void setFat(int val) { fat = val; }
public void setSodium(int val) { sodium = val; }
public void setCarbohydrate(int val) { carbohydrate = val; }
public static void main(String[] args) { NutritionFacts cocaCola = new NutritionFacts(); cocaCola.setServingSize(240); cocaCola.setServings(8); cocaCola.setCalories(100); cocaCola.setSodium(35); cocaCola.setCarbohydrate(27); } }
|
Builder模式
不直接生成想要的对象,而是让调用者利用所有必要的参数调用构造器,得到一个builder对象,然后客户端在builder对象上调用类似于setter
的方法,来设置每个相关的可选参数,最后客户端调用无参的build
方法来生成一个不可变的对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| public class NutritionFacts { private final int servingSize; private final int servings; private final int calories; private final int fat; private final int sodium; private final int carbohydrate;
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(); } }
|
小结####
与构造器相比,builder模式的优势在于,builder可以有多个可变的参数,构造器就像方法一样,只能有一个可变参数。总之,如果类的构造器或静态工厂中具有多个参数,设计这种类的时候,builder模式就是一种不错的选择。
Num3:用私有构造器或者枚举类型强化Singleton属性
Singleton
简单的说就是仅仅被实例化一次的类。实现Singleton
有三种方式。
Field方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class Elvis { public static final Elvis INSTANCE = new Elvis();
private Elvis() { }
public void leaveTheBuilding() { System.out.println("Whoa baby, I'm outta here!"); }
public static void main(String[] args) { Elvis elvis = Elvis.INSTANCE; elvis.leaveTheBuilding(); } }
|
Method方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public class Elvis { private static final Elvis INSTANCE = new Elvis();
private Elvis() { }
public static Elvis getInstance() { return INSTANCE; } public void leaveTheBuilding() { System.out.println("Whoa baby, I'm outta here!"); }
public static void main(String[] args) { Elvis elvis = Elvis.getInstance(); elvis.leaveTheBuilding(); } }
|
特别要说明下,如果要使得类支持可序列化,仅仅加上implements Serializable
是不够的,需要在方法里加上这么一个方法。
1 2 3 4 5
| private Object readResolve() { return INSTANCE; }
|
枚举类方式
1 2 3 4 5 6 7 8 9 10 11 12 13
| public enum Elvis { INSTANCE;
public void leaveTheBuilding() { System.out.println("Whoa baby, I'm outta here!"); }
public static void main(String[] args) { Elvis elvis = Elvis.INSTANCE; elvis.leaveTheBuilding(); } }
|
Num4:消除过期的对象引用
所谓过期引用:是指永远也不会再被解除的引用。在支持垃圾回收的语言中,内存泄露是很隐蔽的,如果一个对象引用被这个对象无意识地保留起来,那么,垃圾回收机制不仅不会处理这个对象,而且也不会处理被这个对象所引用的所有其他对象,从而对性能造成潜在的重大影响。
那么该如何修复呢,很简单:一旦对象引用已经过期了,只需要清空这些引用即可。
1 2 3 4 5 6 7 8
| public Object pop() { if (size == 0) throw new EmptyStackException(); Object result = elements[--size]; elements[size] = null; return result; }
|
一般而言,只要类是自己管理内存,程序员就应该警惕内存泄露问题。
内存泄露的另一个常见问题是缓存,所以一般可以弱引用,weak reference
代表缓存,当缓存过期后,它们会自动被删除,记住缓存项的生命周期是外部引用而不是值引用的。
内存泄露的第三个常见来源是监听器和其他回调。如果你实现了一个API,客户端在这个API中注册回调,却没有显式地取消注册,那么除非采取某些动作,否则就会积聚,确保回调立即被当作垃圾回收的最佳方法是只保存它们的弱引用。