1.单例模式定义?
顾名思义就是在整个运行时域,一个类只有一个实例对象。
2.为什么使用单例设计模式?
因为有的类型的实例对象的创建和销毁对资源来说消耗不大,有的类庞大而且复杂,如果频繁的 创建和销毁对象,并且这些对象是完全可以复用的,那么将会造成一些不必要的性能浪费。
例子:比如现在要写一个访问数据库的demo,而创建数据库链接对象是一个耗资源的操作,并且数据库 链接是完全可以复用的,那么可以将这个对象设计成单例的,这样只需要创建一次并且重复使用这个对象就行了。而不需要每次访问数据库都去创建一个链接对象。
3.在Java中,如何实现单例模式呢?
三点需要考虑:是否线程安全?是不是懒加载?能不能通过反射破坏?
public class Singleton {
//第一种:双检锁写法,有一点复杂
//使用volatile就能组织作用在instance上的指令重排问题
private volatile static Singleton singleton;
private Singleton(){ }//构造器私有,其他类没有办法通过new来构造这个Singleton实例
// private static Singleton instance=null;//初始化对象为null
//加锁,synchronized,才能线程安全,但是每次在获取对象时都要进行同步操作,对性能影响非常大,不可取
public static Singleton getInstance() {//其他类中需要使用Singleton对象的话只能通过调用getInstance方法
//在getInstance方法中,首先判断instance是否被构造过,如果构造过就直接使用,若没有就当场构造
//实例对象是第一次被调用的时候才真正构建的,这种之后构建的方式就叫做懒加载
//懒加载的好处:有的对象构建开销是比较大的,假如这个对象在项目启动时就构建,万一从来都没有被调用过,那就比较浪费
//真正使用了才去创建是更加合理的
//线程不安全,执行判断语句时,可能有多个不同线程同时进入,会执行多次
if (singleton == null){
synchronized (Singleton.class){
if (singleton == null){
singleton=new Singleton();
}
}
}
return singleton;
//只需要在构建对象时同步,而直接使用对象的时候就没有必要同步
//对象进行两次判空的操作叫做双检锁,是一种常见的同步线程手段
/*
happens-before原则:1.程序顺序原则2.锁定原则3.volatile变量原则4.线程启动规则
5.线程结束原则6.中断原则7.终结器原则8.传递性规则
*/
}
}
public class Singleton1 {
//第二种:静态内部类,在程序启动的时候不会加载,只有第一次被调用的时候才会加载
//此方法巧妙运用了Jdk加载机制的特性来 实现懒加载
private static class SingletonHolder{
private static final Singleton1 INSTANCE =new Singleton1();
}
private Singleton1(){}
public static final Singleton1 getInstance(){
return SingletonHolder.INSTANCE;
}
}
//上面几种方式都是可以通过反射来破坏的,但是能反射操作是一种人为的主动操作,只有故意那样操作才会破坏,所以只考虑第一点和第二点是否满足就可以
//对于枚举类型,不能通过反射来获取枚举对象,反射无法获取到它的构造器,因此反射就不能破坏枚举类型的单例,枚举类型本身可以保证线程安全,但是不属于懒加载,它在程序启动之初,就已经把内部的实例完全构建好 来提供给使用者了。目前没有完全满足三个条件的单例写法。第三点基本可以不用考虑。