保证整个程序在一次运行的过程中,被单例模式声明的类的对象要有且只有一个
针对不同的应用场景,单例模式的实现要求也不同
实现步骤:
1.构造方法私有化,保证外界无法直接通过new的方式创建对象;
2.对外提供获取类的实例的静态方法,保证可以有获取的方式
3.在类的内部创建该类的对象,通过提供的公开的静态方法进行返回
public class BasicSingleTon {
//创建唯一实例
private static final BasicSingleTon instance = new BasicSingleTon();
//第二部暴露静态方法返回唯一实例
public static BasicSingleTon getInstance() {
return instance;
}
//第一步构造方法私有
private BasicSingleTon() {
}
}
在其中将BasicSingleTon的实例化对象instance设置为私有的静态的最终的对象,其中的final只是为了保证对象在方法区中的地址无法改变,而对于对象的初始化时机没有影响。最好加上final
//饿汉式
class A{
private static final A instance = new A();
public static A newInstance(){
return instance;
}
private A(){}
}
缺点:初始化属性就创建对象,在未调用时一直占用堆空间内存,浪费内存
//懒汉式
class B{
private static B instance = null;
public static synchronized B newInstance(){
if(instance == null)instance = new B();
return instance;
}
private B(){}
}
缺点:获取对象的静态方法是加锁的,所以并发效率低,浪费时间
利用静态内部类方式的单例实现:
思路:
1.返回该类的对象依赖于一个静态内部类的初始化操作;
2.在这个静态内部类初始化的时候,生成外部类的对象,然后在getInstance中返回
注意这里的初始化是指在JVM 类加载过程中 加载->链接(验证,准备,解析)->初始化 中的初始化。这个初始化过程将为类的静态变量赋予具体的值。
对于一个类的初始化时机有一下几种情况:
1) 使用new关键字实例化对象的时候、读取或设置一个类的静态字段(被final修饰、已在编译期把结果放入常量池的静态字段除外)的时候,以及调用一个类的静态方法的时候。
2)使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化。
3)当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。
//饿懒综合式
class C{
private static class Holder{
static C instance = new C();
}
public static C newInstance(){
return Holder.instance;
}
private C(){}
}
针对于使用synchronized为get对象的静态方法加锁会影响效率的情况,只需要在多加一次判空即可优化效率
代码实现:
public class SyncSingleTon {
private static SyncSingleTon singleTon = null;
public static SyncSingleTon getInstance() {
//这次判空是避免了,保证的多线程只有第一次调用getInstance 的时候才会加锁初始化
if (singleTon == null) {
synchronized (SyncSingleTon.class) {
if (singleTon == null) {
singleTon = new SyncSingleTon();
}
}
}
return singleTon;
}
private SyncSingleTon() {
}
}
枚举实现单例模式的原因是:枚举本身就是线程安全的
思路:
枚举的思想是通过共有的静态final与每个枚举常量导出实例的类,由于不能调用枚举的常量的构造方法去生成对应的对象
public enum EnumSingleTon {
INSTANCE
}
public class SingleTon {
public static void main(String[] args) {
EnumSingleTon instance = EnumSingleTon.INSTANCE;
EnumSingleTon instance1 = EnumSingleTon.INSTANCE;
System.out.println("instance1 == instance = " + (instance1 == instance));//输出结果为 true
}
}
面试问题:
单例模式在面试中会常常的被遇到,因为它是考擦一个程序员的基础的扎实程度的,如果说你跟面试官说你做过项目,面试官让你写几个单例设计模式,你写不出来,你觉着面试官会相信吗?在面试时一定要认真准备每一次面试,靠忽悠即使你被录取了,你也很有可能会对这个公司不满意,好了我们言归正传,其实单例设计模式在面试中很少有人会问饿汉式写法,一般都会问单例设计模式的懒汉式的线程安全问题,所以大家一定要充分理解单例模式的线程安全的问题,就这几种模式花点时间,认真学透,面试中遇到任何关于单例模式的问题你都不会害怕是吧。
Best wish!