Effective Java(一)

来源:互联网 发布:行知中学住宿条件 编辑:程序博客网 时间:2024/06/11 07:34

一.遇到多个构造器参数时要考虑使用构建器
当一个类有多个参数的时候,如果使用构造器初始化实例会很麻烦,这样我们需要提供很多很多构造器,当参数足够多后再加一个参数我们就需要添加很多构造器函数。
此外如果使用重叠构造器模式,会很难理解,如果不小心颠倒了两个参数顺序也很难发现。
当出现这种问题时,我们通常会使用JavaBeans模式,调用一个无参构造器创建对象,然后调用setter方法设置必要参数。这种模式有一个很严重的缺点,因为构造郭恒被分到了几个调用中,在构造过程中JavaBean可能处于不一致的状态。此外,JavaBeans模式阻止了把类做成不可变的可能,这就需要我们自己确保它的线程安全。
在这种情况下,我们可以使用Builder模式保证像重叠构造器模式那样的安全性,也能保证像JavaBeans模式的可读性。示例代码如下:
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 ;     }}
在我的Builder实现中,我会用Builder的构造函数而不是set方法传递客户需要的属性。这样做的好处在于,对象总是能被一次完整的实例化,而不是靠开发人员调用时用set方法补充额外的属性完成实例化。这也体现了不可变性带来的好处。
二.使用枚举类型强化Singleton
传统单例实现:
public class Singleton1 {     private static final Singleton1 instance = new Singleton1();     private Singleton1(){               }     public static Singleton1 getInstance(){           return instance ;     }}
此外还有二次检查锁实现,静态内部类实现等。
从Java1.5版本起,产生另一种Singleton实现方式,使用枚举来实现,代码如下
public enum Singleton2 {     INSTANCE;}
实现起来非常简洁,且是线程安全,也避免了序列化破坏单例。
三.通过私有构造器强化不可实例化的能力
简单来说,我们编写的一些util工具类,不希望被实例化,因为编译器会自动提供一个公有的误餐的构造器,因此我们需要通过私有构造器来避免被实例化,甚至有时候可以在构造器中抛出异常。
四.避免创建不必要的对象
典型示例:判断出生日期是否在1946到1965年之间
public class Person {     private Date birthDate;          public boolean isBabyBoomer(){          Calendar cal = Calendar.getInstance(TimeZone. getTimeZone("GMT" ));           cal.set(1946, Calendar. JANUARY,1,0,0,0);          Date boomStart = cal .getTime();           cal.set(1965, Calendar. JANUARY,1,0,0,0);          Date boomEnd = cal.getTime();           return birthDate .compareTo(boomStart) >= 0 &&                    birthDate.compareTo(boomEnd ) < 0;     }}
isBabyBoomer每次被调用的时候都会创建一个Calendar一个TimeZone和两个Date示例,改进写法:
public class Person {     private Date birthDate;          private static final Date BOOM_START;     private static final Date BOOM_END;     static{          Calendar cal = Calendar.getInstance(TimeZone. getTimeZone("GMT" ));           cal.set(1946, Calendar. JANUARY,1,0,0,0);           BOOM_START = cal .getTime();           cal.set(1965, Calendar. JANUARY,1,0,0,0);           BOOM_END = cal .getTime();     }     public boolean isBabyBoomer(){           return birthDate .compareTo(BOOM_START) >= 0 &&                    birthDate.compareTo(BOOM_END) < 0;     }}
改进后的person类只在初始化的时候创建Calendar,TimeZone和Date示例一次。
五.消除过期的对象引用
如下代码存在内存泄露隐患:
public class Stack {     private Object[] elements;     private int size = 0;     private static final int DEFAULT_INITIAL_CAPACITY = 16;          public Stack(){           elements = new Object[DEFAULT_INITIAL_CAPACITY];     }     public void push(Object e ){          ensureCapacity();           elements[ size++] = e;     }     public Object pop(){           if(size == 0){               return new Exception("栈为空");          }           return elements [--size ];     }     private void ensureCapacity(){           if(elements .length == size ){               elements = Arrays. copyOf(elements,2 * size + 1);          }     }}
内存泄露地方:如果一个栈先是增长,然后再收缩,那么从栈中弹出来的对象将不会被当做垃圾回收,即使使用栈的程序不在引用这些对象。这是因为栈内部维护者对这些对象的过期引用。所谓过期引用,是指永远不会再被解除的引用。在本例中凡是在elements数组的"活动部分"之外任何引用都是过期的。活动部分是指elements中下标小于size的那些元素。
改进方法:一旦对象引用已经火气,清空这些引用。如下所示:
public Object pop(){           if(size == 0){               return new Exception("栈为空");          }          Object result = elements[-- size];           elements[ size] = null;//清空引用,防止内存泄露           return result ;     }

0 0
原创粉丝点击