1.考虑用静态工厂发方法代替构造器
提供静态工厂方法较之提供公有构造器,具有如下几大优势:
- 静态工厂方法与构造器不同的第一大优势在于,他们有名称。当一个类需要多个带有相同签名的构造器时,就用静态工厂方法代替构造器,并且慎重地选择名称以便突出他们之间的区别。
- 静态工厂方法与构造器不同的第二大优势在于,不必每次调用它们的时候都创建一个新对象。可以将构建好的实例缓存起来,进行重复利用,从而避免创建不必要的重复对象。静态工厂方法能够为重复的调用返回相同的对象,这样有助于类总能严格控制在某个时刻那些实例应该存在。这种类被称作实例受控的类。
- 静态工厂方法与构造器不同的第三大优势在于,他们可以返回原返回类型的任何子类型的对象。公有的静态工厂方法所返回的对象的类可以是非公有的(默认访问级别的类只能被同一个包中的其他类访问),而且可以返回任何实现了已声明接口类型的类的实例。
-
静态工厂方法的第四大优势在于,在创建参数化类型实例的时候,他们是代码变得更加简洁。
静态工厂方法的不足:
- 类如果不含公有的或者受保护的构造器,就不能被子类化。
- 静态工厂方法的第二个缺点在于,他们与其他的静态方法实际上没有任何区别。他们在API文档中没有像构造器那样明确标识出来。
2.遇到多个构造器参数时要考虑用构建器
静态工厂和构造器有个共同的局限性:它们都不能很好地扩展到大量的可选参数。
对于拥有大量参数的类(少数必选、多数可选),可采用:
重叠构造器模式(telescoping constructor)
该模式下,你提供第一个只有必要参数的构造器,第二个构造器有一个可选参数,第三个有两个可选参数,依此类推,最后一个构造器包含所有可选参数。
重叠构造器模式可行,但是当有许多参数的时候,客户端代码会很难编写,并且仍然较难阅读。
JavaBeans模式
该模式下,调用一个无参构造器来创建对象,然后调用setter方法来设置每个必要的参数,以及每个相关的可选参数。
JavaBeans模式弥补了构造器模式的不足,其实就是创建实例容易。不过由于构造过程被分到了几个调用中,在构造过程中JavaBean可能出于不一致的状态,类无法仅仅通过检验构造器参数的有效性来保证一致性。此外JavaBean模式阻止了把类做成不可变的可能。
Builder模式
既能保证像重叠构造器模式那样的安全性,也能保证像JavaBeans模式那么好的可读性,
Builder模式不直接生成想要的对象,而是让客户端利用所有必要的参数调用构造器(或者静态工厂),得到一个builder对象,然后客户端在builder对象上调用类似于setter的方法,来设置每个相关的可选参数。最后,客户端调用无参的build方法来生成不可变的对象,这个Builder是他构建的类的静态成员类。
builder像个构造器一样,可以对其参数强加约束条件。将参数从builder拷贝到对象中之后,并在对象域而不是builder域中对他们进行检验。
Builder模式为了创建对象,必须先创建他的构建器,在对性能十分注重的情况下,会造成问题。
简而言之,如果类的构造器或者静态工厂中具有多个参数,设计这种类时,Builder模式就是种不错的选择。特别是当大多数参数都是可选的时候。如果将来会对类添加参数,使用Buider模式也很合适。
3.用私有构造器或者枚举类型强化Singleton属性
Singleton通常被用来代表那些本质上唯一的系统组件,在JDK1.5之前,实现Singleton有两种方法。这两种方法都要把构造器保持为私有的,并导出公有的静态成员,以便允许客户端能够访问该类的唯一实现。
第一种方式:1.构造器私有,2,有一个公共的静态成员,并且是final的。
public class TestSingleton {
private TestSingleton(){
//在此将构造函数设置为private,防止该类在外部被实例化
}
public static final TestSingleton Instance = new TestSingleton();
}
ps:构造方法私有化的作用:外面调用只能调用类A的静态函数A_Func,而不能A a = new A();私有构造函数只能在函数内部调用,外部不能实例化,所以私有构造函数可以防止该类在外部被实例化。
不过上述方式是不能保证绝对的只有一个实例,通过反射机制可以突破这一点,所以该方法不推荐。传说中的 饿汉式的单例模式。
第二种方式:1,构造器私有,2,有一个私有的静态长远,但是有一个公共的静态工厂方法
public class TestSingleton1 {
private static final TestSingleton1 INSTANCE = new TestSingleton1();
private TestSingleton1(){}
public static TestSingleton1 getInstance(){
return INSTANCE;
}
}
这就是传说中的 懒汉式的单例模式,一般情况下,为了防止多线程的时候创建多个实例,都会加上synchronized 关键字。
对于静态方法getInstance()方法的所有调用,都会返回同一个对象的应用。这种写法的好处是很清楚地表明了该类是一个Singleton,但是这种写法有另一个好处是,可以添加其他的静态工厂方法返回另一个新的实例,这样就可以将该类变为非Signleton。
为了使利用上述方法实现Singleton类变成是可序列化的,仅仅在声明时加上“implements Serializable”是不够的。为了保证singleton必须声明所有实例域都是瞬时的(transient),并且还需要提供一个readResolve方法。否则每次反序列化都会创建一个实例。
注意:
为了使利用这其中一种方法实现的Singleton类变成是可序列化的(Serializable),仅仅在声明中加上“implements Serializable”是不够的。为了维护并保证Singleton,必须声明所有实例域都是瞬时(transient)的,并提供一个readResolve方法。
package chapter2.singleton;
// Serializable singleton with public final field - Page 18
public class serializable {
public static final serializable INSTANCE = new serializable();
private serializable() { }
public void leaveTheBuilding() {
System.out.println("Whoa baby, I'm outta here!");
}
private Object readResolve() {
// Return the one true Elvis and let the garbage collector
// take care of the Elvis impersonator.
return INSTANCE;
}
}
从JDK1.5开始,实现Singleton还有第三种方法,只需编写一个包含单个元素的枚举类型。推荐的写法:
public enum Elvis {
INSTANCE;
public void leaveTheBuilding() {
System.out.println("Whoa baby, I'm outta here!");
}
// This code would normally appear outside the class!
public static void main(String[] args) {
Elvis elvis = Elvis.INSTANCE;
elvis.leaveTheBuilding();
}
}
单元素的枚举类型已经成为实现Singleton的最佳方法。
登记式模式(holder)
<span style="font-weight: normal;">public class Singleton {
private Singleton() {} //构造方法是私有的,从而避免外界利用构造方法直接创建任意多实例。
public static Singleton getInstance() {
return Holder.SINGLETON;
}
private static class Holder {
private static final Singleton SINGLETON = new Singleton();
}
}</span>
<span style="font-weight: normal;">public class Singleton {
private Singleton() {} //构造方法是私有的,从而避免外界利用构造方法直接创建任意多实例。
public static Singleton getInstance() {
return Holder.SINGLETON;
}
private static class Holder {
private static final Singleton SINGLETON = new Singleton();
}
}</span>
内部类只有在外部类被调用才加载,产生SINGLETON实例;又不用加锁。此模式有上述前两个模式的优点,屏蔽了它们的缺点,是最好的单例模式。
4.通过私有构造器强化不可实例化的能力
5.避免创建不必要的对象
demo1:
String s = new String("hello world");
需要改成
String s = "hello world";
因为第一种写法,每一次调用的时候都会创建一个新的String实例。但是第二种写法,在同一台虚拟机中运行的代码,只要他们包含相同的字符串字面常量,就会被重用。
对于同时提供了静态工厂方法(见第1条)和构造器的不可变类,通常可以使用静态工厂方法而不是构造器,以避免创建不必要的对象。
6.消除过期的对象引用
那么哪里引起的内存泄漏呢?
如果一个栈先是增长,然后再收缩,那么从栈中弹出来的对象讲不会被当做垃圾回收,即使使用的栈程序不再引用这些对象。
这是因为,栈内部维护着对这些对象的过期引用(obsolete reference)。过期引用是指,永远也不会再被接触的引用。
修复的方法:
一旦对象引用已经过期,只需清空这些引用即可。清空对象引用应该是一种例外,而不是一种规范行为。
消除过期引用最好的方法是让包含该引用的变量结束其生命周期。
那么,合适应该清空引用呢?
问题在于,Stack类自己管理内存。存储池包含了elements数组的元素。数组活动区域中的元素是已分配的,而数组其余部分的元素则是自由的。但是对于垃圾回收器而言,elements数组中的所有对象引用都同等有效。
一旦数组元素变成了非活动部分的一部分,程序员就收工清空这些数组元素。
只要类是自己管理内存,程序员就应该警惕内存泄漏问题。
缓存
LinkedHashMap类的removeEldestEntry(Map.Entry<K,V> eldest) 此方法非常有用:它允许映射通过删除旧条目来减少内存损耗。监听器和其他回调
如果你实现了一个API,客户端在这个API中注册回调,却没有显示的取消注册,那么除非你采取某些动作,否则他们就会积聚。最佳方法是只保存他们的弱引用(weak reference),例如,只将他们保存为WeakHashMap中键。
7.避免使用终结方法
Java语言规范不仅不保证终结方法会被及时的执行,而且根本不保证他们会被执行。
不应该依赖终结方法来更新重要的持久状态。
不要被System.gc和System.runFinalization这两个方法所诱惑,他们确实增加了终结方法被执行的机会,但是他们不保证终结方法一定被执行。唯一声称保证终结方法被执行的方法是System.runFinalizersOnExit,以及他臭名昭著的孪生兄弟Runtime.runFinalizersOnExit。这两个方法都有致命的缺陷,都被废弃了。
使用终结方法有一个非常严重的(Severe)性能损失。
显示终止方法的典型例子是InputStream、OutputStream和java.sql.Connection上的close方法。
另一个例子是java.util.Timer上的cancel方法,它执行必要的状态改变,使得与Timer实例相关联的该线程温和的终止自己。
java.awt中的例子还包括Graphics.dispose和Window.dispose。
Image.flush,他会释放所有与Image实例相关联的资源,但是该实例仍然处于可用的状态,如果有必要的话,会重新分配资源。
总结:除非作为安全网,或者是为了终止非关键的本地资源,否则请不要使用终结方法。在这些很少见的情况下,既然使用了终结方法,就要记住调用super.finalize。如果用终结方法作为安全网,要记得记录终结方法的非法用法。最后,如果需要吧终结方法与公有的非final类关联起来,请考虑使用终结方法守护者,以确保即使子类的终结方法未能调用super.finalize,该终结方法也会被执行。