java创建和销毁对象。
一、考虑用静态工厂方法代替构造器
1.静态工厂方法的优势
(1)易读性
例如:java.math.BigInteger构造器BigInteger(int,int,Random)返回的
BigInteger可能为素数;如果用名为BigInteger.probablePrime的静态工厂方法来表示,则简单易懂。
(2)不必每次调用它们的时候都创建一个新的对象。
(3)可以返回原返回类型的任何子类型的对象。
(4)在创建参数化类型实例的时候,使代码变得更加简洁。
2. 静态工厂方法的缺点
(1)类如果不含公有的或者受保护的构造器,则不能被子类继承。
(2)它们与其他的静态方法实际上没有任何区别。
静态工厂方法的惯用名称:
l valueOf 该方法返回的实例与它的参数具有相同的值。这样的静态工厂方法实际上是类型转换方法。
例如:java.lang.Boolean 里面的public static Boolean valueOf(boolean b) {return (b ? TRUE : FALSE);
}
l of valueOf的一种更为简洁的替代,在java.util.EnumSet中使用
并流行起来。
public static <E extendsEnum<E>> EnumSet<E> of(E e) {
EnumSet<E> result = noneOf(e.getDeclaringClass());
result.add(e);
return result;
}
l getInstance 返回的实例是通过方法的参数来描述的,对于Singleton来说,该方法没有参数,并返回唯一的实例。
l newInstance 像getInstance一样,但是newInstance能够确保返回的每一个实例都与所有其他实例不同。
l getType 在工厂方法处于不同的类中的时候使用;Type表示工厂方法所返回的对象类型。
l newType 在工厂方法处于不同的类中的时候使用;Type表示工厂方法所返回的对象类型。
简而言之,静态工厂方法和公有的构造器各有用处,我们需要理解它们各自的长处,静态工厂通常更加合适,因此切忌第一反应就是提供公有的构造器,而不先考虑静态工厂。
二、当构造器的参数很多时考虑使用构建器(Builder模式)
静态工厂和构造器有个共同的局限性:它们都不能很好的扩展大量的可选参数。
例如:用一个类表示包装食品外面显示的营养成分标签。这些标签中有几个域是必填的:每份的含量、每罐的含量以及每份的卡路里,还有超过20个可选域:总脂肪量、饱和脂肪量、转化脂肪、胆固醇、钠等。
大多数产品在某几个可选域中都会有非零的值。
不直接生成想要的对象,让客户端利用所有必要的参数调用构造器(或者静态工厂)得到一个builder对象。然后客户端在builder对象上调用类似于setter的方法,来设置每个相关的可选参数
package com.test;
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 String toString(){
StringBuffer buf = new StringBuffer();
buf.append(“servingSize=”).append(servingSize).append(“\n”);
buf.append(“servings=”).append(servings).append(“\n”);
buf.append(“calories=”).append(calories).append(“\n”);
buf.append(“fat=”).append(fat).append(“\n”);
buf.append(“sodium=”).append(sodium).append(“\n”);
buf.append(“carbohydrate=”).append(carbohydrate).append(“\n”);
return buf.toString();
}
private NutritionFacts(Builder builder){
servingSize = builder.servingSize;
servings = builder.servings;
calories = builder.calories;
fat = builder.fat;
sodium = builder.sodium;
carbohydrate = builder.carbohydrate;
}
public static class Builder{
//必填参数
private final int servingSize;
private final int servings;
//可选
private int calories = 0;
private int fat = 0;
private int sodium = 0;
private int carbohydrate = 0;
public Builder(int servingSize,int servings){
this.servingSize = servingSize;
this.servings = servings;
}
public Builder setCalories(int val){
calories = val;
return this;
}
public Builder setFat(int val){
fat = val;
return this;
}
public Builder setSodium(int val){
sodium = val;
return this;
}
public Builder setCarbohydrate(int val){
carbohydrate = val;
return this;
}
public NutritionFacts build(){
return new NutritionFacts(this);
}
}
}
三、用私有构造器或者枚举类型强化Singleton属性
参见http://blog.csdn.net/chaobin0524/article/details/18980459
四、通过私有构造器强化不可实例化的能力
java中的一些工具类例如java.lang.Math或者java.util.Arrays不希望被实例化,构造器定义为私有的。
// Suppresses default constructor, ensuring non-instantiability.
private Arrays() {
throw new AssertionError();
}
构造器是私有的,所以不可以在该类的外部访问它,AssertionError不是必须的,但是它可以避免不小心在类的内部调用构造器。它保证该类在任何情况下都不会被实例化。这种习惯用法有点违背直觉,好像构造器就是专门设计成不能被调用一样;因此,明智的做法是在代码中增加一条注释”//Suppresses …”。
五、避免创建不必要的对象
最好能重用对象而不是在每次需要的时候就创建一个相同功能的新对象。对象重用既快速,又流行。如果对象是不可变的,它就始终可以被重用。
例如:String s = new String(“stringtest”);
这个语句创建了两个String对象。
对于所有在同一台虚拟机中运行的代码,只要它们包含相同的字符串字面常量,该对象就会被重用。
六、消除过期对象的引用
过期对象的引用,容易造成“内存泄露”问题。
消除过期引用最好的办法是让包含该引用的变量结束其生命周期。
只要类是自己管理内存,程序员就应该警惕内存泄露问题。
内存泄露的另一个常见来源是缓存。
内存泄露的第三个常见来源是监听器和其他回调。
七、避免使用终结方法
终结方法(finalizer)通常是不可预期的,也是很危险的,一般情况下是不必要的。
从一个对象变得不可到达开始,到它的终结方法被执行,所花费的这段时间是任意长的,
用终结方法来关闭已经打开的文件,这是严重的错误。
不应该依赖终结方法来更新重要的持久状态。例如:依赖终结方法来释放共享资源
(比如:数据库)上的永久锁,很容易让整个分布式系统垮掉。
不要被System.gc和System.runFinalization这两个方法所诱惑,它们确实增加了终结方法被执行的机会,但是它们并不保证终结方法一定会被执行。
终结方法的两种合法用途:
1.充当“安全网(safetynet)” 当对象的所有者忘记调用前面段落中建议的显示终止方法时,终结方法可以充当安全网,虽然这样做并不能保证终结方法会被及时地调用,但是在客户端无法通过调用显式的终止方法来正常结束操作的情况下,迟一点释放关键资源总比永远不释放要好。显式终止方法模式(FileInputStream,FileOutputStream,Timer和Connection),都具有终结方法,当它们的终止方法未能被调用的情况下 ,这些终结方法充当了安全网。
2.终止非关键的本地资源