No1 考虑用静态工厂方法代替构造器
静态工厂方法优势:
- 它们有名称,阅读性增强,如:BigInteger.probablePrime;
- 不必每次调用它们的时候都创建一个新对象;
- 它们可以返回原返回类型的任何子类型的对象;
- 在创建参数化类型实例的时候,它们使代码变得更加简洁。
静态工厂方法缺点:
- 类如果不含公有的或者受保护的构造器,就不能被子类化;
- 它们与其他的静态方法实际上没有任何区别。
惯用名称:
- getInstance 返回的实例是通过方法的参数来描述的;
- newInstance 像getInstance一样,但newInstance能够确保返回的每个实例都与所有其它实例不同。
No2 遇到多个构造器参数时要考虑用构建器
2.1多个构造器(缺点:许多参数时,客户端代码难写;不易于阅读);
// Telescoping constructor pattern - does not scale well! - Pages 11-12 public class NutritionFacts { private final int servingSize; // (mL) required private final int servings; // (per container) required private final int calories; // optional private final int fat; // (g) optional private final int sodium; // (mg) optional private final int carbohydrate; // (g) optional 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); } }
2.2 JavaBeans模式(缺点:对象可能处于不一致的状态);
// JavaBeans Pattern - allows inconsistency, mandates mutability - Pages 12-13 public class NutritionFacts { // Parameters initialized to default values (if any) private int servingSize = -1; // Required; no default value private int servings = -1; // " " " " private int calories = 0; private int fat = 0; private int sodium = 0; private int carbohydrate = 0; public NutritionFacts() { } // Setters 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); } }
2.3 Builder模式(用一个专门的对象来存放参数,形如:new NutritionFacts(Builder builder),所有参数封装在Builder类中)。
// Builder Pattern - Pages 14-15 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 { // Required parameters private final int servingSize; private final int servings; // Optional parameters - initialized to default values 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(); } }
No5 避免创建不必要的对象
5.1 String对象的使用
String s = new String(“stringetee”); // DON’T DO THIS String s = “stringetee”; // 正确方式,引用常量池中的对象
5.2 注意某些方法调用频繁生成对象时,应考虑将这些对象放置到类(对象)的局部变量中。
不好的代码如下:
// Creates lots of unnecessary duplicate objects - page 20-21 import java.util.*; public class Person { private final Date birthDate; public Person(Date birthDate) { // Defensive copy - see Item 39 this.birthDate = new Date(birthDate.getTime()); } // Other fields, methods omitted // DON'T DO THIS! public boolean isBabyBoomer() { // Unnecessary allocation of expensive object Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT")); gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0); Date boomStart = gmtCal.getTime(); gmtCal.set(1965, Calendar.JANUARY, 1, 0, 0, 0); Date boomEnd = gmtCal.getTime(); return birthDate.compareTo(boomStart) >= 0 && birthDate.compareTo(boomEnd) < 0; } }
优化后的代码如下:
// Doesn't creates unnecessary duplicate objects - page 21 import java.util.*; class Person { private final Date birthDate; public Person(Date birthDate) { // Defensive copy - see Item 39 this.birthDate = new Date(birthDate.getTime()); } // Other fields, methods /** * The starting and ending dates of the baby boom. */ private static final Date BOOM_START; private static final Date BOOM_END; static { Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT")); gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0); BOOM_START = gmtCal.getTime(); gmtCal.set(1965, Calendar.JANUARY, 1, 0, 0, 0); BOOM_END = gmtCal.getTime(); } public boolean isBabyBoomer() { return birthDate.compareTo(BOOM_START) >= 0 && birthDate.compareTo(BOOM_END) < 0; } }
5.3 JDK1.5之后,自动装箱功能可能导致代码BUG。
如下代码:
// Hideously slow program! Can you spot the object creation? public static void main(String[] args) { Long sum = 0L;
for (long i = 0; i < Integer.MAX_VALUE; i++) { sum += i; } System.out.println(sum); }
这段程序算出的结果是正确的,但是比实际情况要更慢一些,只因为打错了一个字符。变量sum被声明成Long而不是long。要优先使用基本类型而不是装箱基本类型,要当心无意意识的自动装箱。
No7 避免使用终结方法
Java语言规范不仅不保证终结方法会被及时地执行,而且根本就不保证它们会被执行。
显式终止方法的典型盒子是InputStream,OutputStream和java.sql.Connection上的close方法。另一个例子是java.util.Timer上的cancel方法,它执行必须的状态改变,使得与Timer实例相关联的该线程温和地终止自己。
显式的终止方法通常与try-finally结构结合起来使用,以确保及时终止。