78条索引
第一条:考虑用静态工厂方法代替构造器
静态工厂方法与构造器不同的第一大优势在于,他们有名称。
静态工厂方法与构造器不同的第二大优势在于,不必在每次调用的时候都创建一个新的对象。
静态工厂方法与构造器不同的第三大优势在于,它们可以返回原返回类型的任何子类型的对象。
静态工厂方法与构造器不同的第三大优势在于,在创建参数化类型实例的时候,它们使代码变得更简洁。
静态工厂方法的主要缺点在于,类如果不含有公有的或者受保护的构造器,就不能被子类化。
静态工厂方法的第二个缺点在于,它们与其他的静态方法实际上没有任何区别。
第二条:遇到多个构造器参数时要考虑用构建器
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;
private int fat;
private int sodium;
private int carbohydrate;
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) {
servings=builder.servings;
servingSize=builder.servingSize;
calories=builder.calories;
fat=builder.fat;
carbohydrate=builder.carbohydrate;
sodium=builder.sodium;
}
}
public class Test {
public static void main(String[] args) {
NuTritionFacts nut=new NuTritionFacts.Builder(20, 30).fat(200).build();
}
}
第三条:用私有构造器或者枚举类型强化Singleton属性
实现Singleton的三种方法
1、利用共有的final静态成员实现
public class Elvis1 {
// 利用final的共有静态成员实现
public static final Elvis1 INSTANCE = new Elvis1();
private Elvis1() {
}
public void leaveBuild() {
}
}
2、静态工厂方法实现
public class Elvis2 {
private static final Elvis2 INSTANCE = new Elvis2();
private Elvis2() {
}
public static Elvis2 getInstance() {
return INSTANCE;
}
public void leaveBuild() {
}
}
3、使用枚举方法实现
public enum Elvis3 {
INSTANCE;
public void leaveBuild() {
}
}
4、客户端
public class Test {
public static void main(String[] args) {
Elvis1 e1= Elvis1.INSTANCE;
e1.leaveBuild();
Elvis2 e2=Elvis2.getInstance();
e2.leaveBuild();
Elvis3 e3=Elvis3.INSTANCE;
e3.leaveBuild();
}
}
第四条:通过私有构造器强化不可实例化的能力
第五条:避免创建不必要的对象
最好能重用对象,而不是需要的时候就创建一个。
不要为了避免创建对象而使用对象池,除非池中对象时重量级的。
第六条:消除过期的对象引用
避免造成内存泄漏,其一是数组中不再被用到的元素。
内存泄漏的另一常见来源是缓存,容易被遗忘。
内存泄漏的第三个常见来源是监听器和其他回调。
第七条:避免使用终结方法
因为终结方法的优先级很低。
第八条 : 覆盖equals时请遵守通用约定
1、自反性(reflexive)。对于任何非null的引用值,x.equals(x)必须返回true。
2、对称性(symmetric)。对于任何非null的引用值x和y,当且仅当y.equals(x)返回true时,x.equals(y)必须返回true。
3、传递性(transitive)。对于任何非null的引用值x、y和z,如果x.equals(y)返回true,并且y.equals(z)也返回true,那么x.equals(z)也必须返回true。
4、一致性(consistent)。对于任何非null的引用值x和y,只要equals的比较操作符在对象中所用的信息没有被修改,多次调用x.equals(y)就会一致的返回true,或一直的返回false。
5、对于任何非null的引用值x,x.equals(null)必须返回false。
提高equals方法质量的诀窍
1、使用==操作符检查“参数是否为这个对象的引用”。
2、使用instanceof操作符检查“参数是否为正确类型”。
3、把参数转为正确类型。
4、对于类中的每个关键(significant)域,检查参数中的域是否与该对象中对应的域相匹配。
5、遵守通用约定。
- 覆盖equals时总要覆盖hashcode。
- 不要企图让equals方法过于智能。
- 不要将equals声明中的object对象替换为其他类型。
第九条 :覆盖equals时总要覆盖hashcode。
散列函数的简单实现:
-
把某个非零的常数值,比如说17,保存在一个名为result的int类型的变量中。
-
对于对象中的每个关键域(指equals方法涉及的每个域),完成以下步骤。
a.为该域计算int类型的散列码c:
i.如果是boolean类型,则计算(f?1:0);
ii.如果该域是byte、char、short或者int类型,则计算(int)f。
iii.如果该域是long类型,则计算(int)(f^(f>>>32))。
iv.如果该域是float类型,则计算Flaot.floatToIntBits(f)。
v.如果该域是double类型,则计算Double.doubleToLongBits(f)。
vi.如果该域是一个对象引用,并且该类的equals方法通过递归的调用equals的方式来比较这个域,则同样的为这个域递归的调用hashcode。如果需要更复杂的比较,则为这个域计算一个范式(canonical representation),然后针对这个范式调用hashcode。如果这个值域是null,则返回0;
vii.如果该域是一个数组,则需要把每一个元素当做单独的域来处理。也就是说递归的应用上述规则,对每个重要的元素计算一个散列码,然后根据步骤2.b的做饭把这些散列值组合起来。如果数组域中的每个元素都很重要,可以利用arrays.hashcode方法。
b.按照下面公式,把步骤2.a中计算得到的散列码c合并到result中:
result=31*result+c. -
返回result
-
写完之后验证,相等的实例是否有相同的散列码。
第十条:始终要覆盖toString方法。
- toString方法可以让对象更具有可读性,提高开发的效率,比如BigInteger和BigDecimal。
- toString 的坏处就是,如果在其他地方过多的使用toString方法来获取对象的指定格式的字符串,那么在后期对于对象的修改将会变得难以维护。
第十一条 :谨慎的的覆盖clone
- Cloneable接口的目的是作为一个对象的mixin interface,表明对象允许克隆。但是Object的clone方法是受保护的,所以clone接口的实现需要具有规范的实现,以保证clone的正常执行。
- clone的通用约定:
x.clone()!=x;
x.clone().getclass()==x.getclass();
x.clone().equals(x);
clone的约定是非常弱的。
3. 建议使用拷贝工厂或拷贝构造器来实现clone的功能,不需要遵守clone的规范,同时过程是可控的
public Yum(Yum yum);
public static Yum newInstance(Yum yum);
第十二条:考虑实现Comparable接口
- 实现Compareable接口,它就可以和许多泛型算法以及依赖于该接口的集合实现进行协作。
- CompareTo约定的条款(自反性、对称性、自反性):
第一条指出,如果颠倒了两个对象之间的的比较方向,就会发生下面的情况:如果第一个对象小于第二个对象,则第二个对象一定大于第一个对象;如果第一个对象等于第二个对象,则第二个对象一定等于第一个对象;如果第一个对象大于第二个对象,则第二个对象一定小于第一个对象。
第二条指出,如果第一个对象大于第二个对象,并且第二个对象又大于第三个对象,那么第一个对象一定大于第三个对象。
最后一条指出,在比较被认为相等的所有对象,他跟别的对象做比较时一定会产生相同的结果。 - 注意:整数基本数类型时可以使用<>操作符,但是在浮点数时,应使用Double.compare()或者Float.compare()。
第十三条:使类和成员的可访问性最小化
1、非final类型或者final指向了可变对象的引用时,便失去了强制这个域不可变的能力。
2、注意,长度非零的数组总是可变的。