顾名思义,单例对象能保证在一个JVM中,该对象只有一个实例存在。
饿汉式:立即记载,线程安全,对象过早创建容易占用内存,资源浪费
public class Singleton {
//创建 SingleObject 的一个对象
private static Singleton singleton = new Singleton();
//让构造函数为 private,这样该类就不会被实例化
private Singleton(){}
//获取唯一可用的对象
public static Singleton getSinglton(){
return singleton;
}
public void showMessage(){
System.out.println("Hello World!");
}
}
调用:
public class SingletonPatternDemo {
public static void main(String[] args) {
//不合法的构造函数
//编译时错误:构造函数 SingleObject() 是不可见的
//SingleObject object = new SingleObject();
//获取唯一可用的对象
Singleton object = Singleton.getSinglton();
//显示消息
object.showMessage();
}
}
1,为什么要创建一个private Singlton(){}无参构造?
禁止其他程序创建该类的对象
2,为什么本类new出来的对象要私有和静态化?
私有是不能直接访问,通过指定方法访问,也是封装的体现。静态修饰是因为单例对象singlton能够被外部调用,调用方法getSinglton是静态的,所以对象必须也是静态的
懒汉式
延迟加载,降低内存消耗,线程不安全,如果想要线程安全需要增加sycronized关键字
public class Singleton {
//创建 SingleObject 的一个对象
private static Singleton singlton;
//让构造函数为 private,这样该类就不会被实例化
private Singleton(){}
//获取唯一可用的对象
public static Singleton getSinglton(){
if(singlton==null){
singlton = new Singleton();
}
return singlton;
}
public void showMessage(){
System.out.println("Hello World!");
}
}
加了锁之后懒汉式效率会降低,如何提高效率?
DCL双检查锁机制。
1,拿到锁之前检查是否是空对象,2,new对象之前检查是否是空对象
为什么拿锁前要判断?因为要提高效率,已有对象直接跳过,不用去争夺锁
为什么new对象之前还要判断?因为这里的singlton是Singlton对象的引用,前一个线程如果已经创建了对象那么singlton就不是null,不做判断下一个线程进来就会重复new对象,就不是单例了。
为什么要加volatile关键字?
因为假设一个线程new出一个对象的时候,还没执行init()构造方法,就发生了指令重排序,没来的及赋值就指向了对象的引用,这个值就是0。此时另一个线程来了,判断对象不为空就拿去用了,使用了半初始化状态的对象。为了避免发生这种问题所以加入volatile关键字
public class Singleton { //创建 SingleObject 的一个对象 private static volatile Singleton singleton; //让构造函数为 private,这样该类就不会被实例化 private Singleton(){} //获取唯一可用的对象 public static Singleton getSinglton(){ // 第一次检查instance是否被实例化出来,如果没有进入if块 if(singleton == null) { synchronized (Singleton.class) { // 某个线程取得了类锁,实例化对象前第二次检查instance是否已经被实例化出来,如果没有,才最终实例出对象 if (singleton == null) { singleton = new Singleton(); } } } return singleton; } public void showMessage(){ System.out.println("Hello World!"); } }
JDK的动态代理使用Java的反射技术生成动态代理类,只能代理实现了接口的类, 没有实现接口的类不能实现动态代理。
CGLIB动态代理运行时动态的生成一个被代理类的子类(通过ASM字节码处理框架实现),子类重写了被代理类中所有非final的方法,在子类中采用方法拦截的技术拦截所有父类方法的调用,不需要被代理类对象实现接口,从而CGLIB动态代理效率比Jdk动态代理反射技术效率要高。
四,工厂模式
一个接口有多个实现类的情况下,具体创建哪个实现类的对象交由工厂去管理,也就是当需要用哪个子类的时候就由工厂去new哪个子类