《Effective Java》读书笔记一(创建与销毁对象)

No1 考虑用静态工厂方法代替构造器

静态工厂方法优势:

  1. 它们有名称,阅读性增强,如:BigInteger.probablePrime;
  2. 不必每次调用它们的时候都创建一个新对象;
  3. 它们可以返回原返回类型的任何子类型的对象;
  4. 在创建参数化类型实例的时候,它们使代码变得更加简洁。

静态工厂方法缺点:

  1. 类如果不含公有的或者受保护的构造器,就不能被子类化;
  2. 它们与其他的静态方法实际上没有任何区别。

惯用名称:

  • 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结构结合起来使用,以确保及时终止。

上一篇:在 .Net 项目中生成Report小记


下一篇:浅谈Log4net在项目中如何记录日志