引言
在我们的程序设计中,通常会需要一个只有一个实例的单例类,设计单例类可以让我们在访问一个固定的元素时不必创建多余的对象,或多余相同的对象,以避免造成对象的会乱和额外性能开销。
典型的应用场景是数据库连接池对象:在一个项目的诸多地方都要连接数据库进行操作,而每个类都独立的创建并连接数据库必然会造成巨大的开销,那么我们就可以设计一个单例类,这个类中存放数据库连接相关的对象,那么每个其他的类都可以通过访问这个唯一的单例去进行数据库操作。
设计代码
我们最先也最容易想到的方式即如下所示:
class SingleTon{
public static final SingleTon instance = new SingleTon();
private SingleTon(){
// . . .
}
}
设置一个公有的静态final类,再设置一个私有构造方法,由于static的变量属于类,因此只会在类被加载时被初始化一次,再通过外部访问instance变量去获得这个单例类。
这确实是一个单例类设计的不错思路,但我们可以让它变得更安全。
由于享有特权的用户可以借助AccessibleObject.setAccessible方法,通过反射机制调用私有构造器,因此对于这种攻击,上述方式就会被创建出另一个该单例类的对象。
因此我们可以改进这个单例类的设计,将公有成员设计成一个静态工厂方法:
class SingleTon{
private static SingleTon instance;
private SingleTon(){
if(instance != null)
System.out.println("the only one instance already created.");
else
// . . .
}
public static SingleTon getInstance(){
if(instance == null)
instance = new SingleTon();
return instance;
}
}
可以看到,我们首先不再在类初始化时就生成这个单例对象,只在调用getInstance时创建第一个对象;其次,我们设计了一个逻辑,使该单例类只有在没有实际的单例对象时才会进入创建逻辑;并且,我们也用了一个逻辑去防范非常规的构造方法调用所带来的错误。
经验总结
虽然提升这个单例类的安全性使得代码变得比原来复杂的多,但这是非常有益的,因为现代JVM有着高度优化过的垃圾回收器,使得轻量级的对象创建与回收效率相比维护一个自己定义的对象池其实在效率上要更好一些,所以我们只会在对一些非常重量级的对象设计上使用单例设计模式,而这些重量级的对象往往在安全上也非常的核心和重要,因此通过必要的逻辑保证安全性是非常重要的。