一、考虑用静态工厂代替构造器(实用程度◆)
二、遇到多个构造器参数时优先考虑用构建器(实用程度◆◆◆)
三、用私有构造器或枚举类型强化Singleton属性(实用程度◆◆◆)
四、通过私有构造器强化不可实例化的能力(实用程度◆◆◆◆◆)
五、避免创建不必要的对象(实用程度◆◆◆◆)
六、消除过期的对象引用(实用程度◆◆)
七、避免使用终结方法(实用程度◆)

一、考虑用静态工厂代替构造器(实用程度◆)
1.与构造器相比的优势
⑴它们有名称
⑵不必每次调用它们的时候都创建一个新对象
⑶它们可以返回返回类型的任何子类型对象
⑷它们使代码变的更加简洁
2.与构造器相比的劣势
⑴类如何不含有公有的或者收保护的构造器,就不能被子类化。
⑵它们与其他的静态方法实际上没有任何区别
二、遇到多个构造器参数时优先考虑用构建器(实用程度◆◆◆)
这里我们可以用lombok的模式,也可以自己手写。推荐采用lombok的方式。
package Chapter2_2; /*** * @author bincai * @email [email protected] */ 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
Buildercarbohydrate (int val) { carbohydrate = val; return this; } public
Buildersodium (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(); } }
三、用私有构造器或枚举类型强化Singleton属性(实用程度◆◆◆)
⑴首先我们先看两种常见的单例模式
public class Elvis{ public static final Elvis INSTANCE = new Elvis(); private
Elvis(){...} ... public void singASong(){...} }
上面的方式客户端可以通过反射机制调用私有构造器
public class Elvis{ private static final Elvis INSTANCE = new Elvis(); private
Elvis(){...} public static Elvis getInstance(){ return INSTANCE; } ... public
void singASong(){...} }
上面的方式如果要实现序列化而不破坏单例,需要一个readResolve方法
private Object readResolve(){ //Return the one true Elvis and let the garbage
collector take care of the Elvis impersonator return INSTANCE; }
我们采用枚举的方式:
public enum Elvis(){ INSTANCE; ... public void singASong(){...} }
四、通过私有构造器强化不可实例化的能力(实用程度◆◆◆◆◆)
有些工具类只有静态方法和静态成员变量,这些类不需要被实例化
public class UtilityClass{ // Suppress default constructor for
noninstantiability // (Add comment to clarify why the constructor is expressly
provided) private UtilityClass(){ throw new AssertionError(); } ... }
五、避免创建不必要的对象(实用程度◆◆◆◆)
1.重用不可变的对象
我们应该这样:
String s = "stringette";
而不是这样:
String s = new String("stringette");
2.使用静态工厂方式比构造器方式更好(参见第一段)
3.一般可以也可以重用那些明知不会被改变的对象
有一个人我们检测他是否出生于1946至1964之间
我们应该这样:
public class Person { private final Date birthDate; ... 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; } }
而不是这样,下面的方式每次都会新建一个Calender,一个TimeZone,和两个Date实例
public class Person { private final Date birthDate; ... 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; } }
4维护自己的对象池一般不是好的做法,除非对象是非常重量级的,比如数据库连接池。

六、消除过期的对象引用(实用程度◆◆)
你能看出下面代码哪里内存泄漏了吗?
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)
throw new EmptyStackException(); return elements[--size]; } private void
ensureCapacity(){ if(elements.length == size) elements = Array.copyOf(elements,
2 * size + 1); } }

如果是一个栈先增长,然后再收缩,那么栈中弹出来的对象不会当做垃圾回收,即使栈中的程序不再引用这些对象,因为栈内部维护着对这些对对象的过期引用,所谓过期引用,是指永远也不会再被解除的引用。这里所有在elements数组的不小于size的元素都的引用是过期的。我们可以修改pop来防止内存泄漏:
public pop(){ if (size == 0) throw new EmptyStackException(); Object result =
elements[--size]; elements[size] =null; // Eliminate obsolete references. return
result; }
总之:
⑴只要类是自己管理内存的,就该警惕内存泄漏

⑵缓存也容易导致内存泄漏,为了防止内存泄漏,可以采用weakhashmap,如果要保证缓存在没有意义时被回收,那么可以搞个后台线程进行清理,或者在给缓存添加新条目时候进行清理,linkedhashmap的removeEldestEntry可以实现后一种方案,更加复杂的缓存就要java.lang.ref了
⑶监听器和其它回调也容易导致内存泄漏
我们可以将它们保存成weakhashmap中的键。
七、避免使用终结方法(实用程度◆)
Finalizers一般是不可预测且危险的。
我们在流处理,数据库连接上也要显示关闭,那么可以采用下面的方法:
Foo foo = new Foo(...); try { // Do what must be done with foo ... } finally
{ foo.terminate();// Explicit termination method }

友情链接
KaDraw流程图
API参考文档
OK工具箱
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:[email protected]
QQ群:637538335
关注微信