介绍
单例模式的实现
饿汉式:类加载就会导致该单实例对象被创建。在类加载时,不管用不用,这个对象都会在内存中,会造成内存的浪费。
方式1:静态变量方式
package 创建者模式.单例模式; //单例模式 饿汉式 静态成员变量 public class Singleton { //1.私有构造方法,单例模式允许有一个对象,外界无法访问就不能创建对象 private Singleton(){} //2.在本类中创建本类对象,在静态方法中要返回对象,所以这个属性需要时静态的 private static Singleton instance = new Singleton(); //3.提供一个公共的访问该对象的方式让外界获取该对象 //外界不能创建这个对象,没有对象就不能调用普通方法,所以要用类名.静态方法名的方式来调用方法,从而获取对象 public static Singleton getInstance(){return instance;}; }package 创建者模式.单例模式; public class Client { public static void main(String[] args) { //创建Singleton类的对象 Singleton instance = Singleton.getInstance(); Singleton instance1 = Singleton.getInstance(); //判断获取的两个对象是否是同一个 System.out.println(instance == instance1);//输出true } }方式2:静态代码块方式
package 创建者模式.单例模式饿汉式静态代码块方式; public class Singleton { //1.构造私有方法 private Singleton(){}; //2.Singleton对象 private static Singleton instance; //3.为该对象赋值 static { instance = new Singleton(); } public static Singleton getInstace(){ return instance; } }package 创建者模式.单例模式饿汉式静态代码块方式; public class Client { public static void main(String[] args) { Singleton instance = Singleton.getInstace(); Singleton instance1 = Singleton.getInstace(); //判断是否做到单例 System.out.println(instance1 == instance);//true } }方式3:枚举方式
枚举类型线程安全,并且只会加载一次
public enum Singleton { INSTANCE; }public class Client { public static void main(String[] args) { Singleton instance = Singleton.INSTANCE; Singleton instance1 = Singleton.INSTANCE; System.out.println(instance1 == instance);//true } }懒汉式:类加载不会导致该单实例对象被创建,而是首次使用该对象时被创建。
方式1:线程不安全
package 创建者模式.单例模式懒汉式线程不安全方式; public class Singleton { private Singleton(){}; //这里只是声明了一个类型的变量,没有进行赋值 private static Singleton instance; public static Singleton getInstance(){ if(instance == null){ instance = new Singleton(); } return instance; } }方式2:线程安全
package 创建者模式.单例模式懒汉式线程不安全方式; public class Singleton { private Singleton(){}; //这里只是声明了一个类型的变量,没有进行赋值 private static Singleton instance; //与方式1的区别就是加了关键字 synchronized public static synchronized Singleton getInstance(){ if(instance == null){ instance = new Singleton(); } return instance; } }方式3:双重检查锁
对于方法2,由于获取了一次对象后,以后每次判断就会是false了,所以每次都不用执行if里面的语句,无论哪个线程在执行return时都会是安全的,只有在未确定对象时,线程才会不安全,但每次每个线程都要进入锁就会影响代码效率,所以要调整加锁时机。所以有了双重检查。
对synchronized用法不明白看synchronized 参数 及其含义_知我饭否-CSDN博客_synchronized参数
package 创建者模式.单例模式饿汉式双重检查锁; public class Singleton { private Singleton(){}; private static Singleton instance; public static Singleton getInstance(){ //第一次判断,如果instance值不为null,不需要抢占锁,直接返回对象。 if(instance == null){ //锁的对象是当前类的字节码对象 synchronized (Singleton.class){ //第二次判断 if(instance == null){ instance = new Singleton(); } } } return instance; } }在声明instance时,在static后面加上volatile
方式4:静态内部类方式
静态内部类单例模式中实例由内部类创建,由于JVM加载外部类的过程中,是不会加载静态内部类的,只有内部类的属性/方法被调用时才会被加载。并初始化其静态属性。静态属性由于被static修饰,保证只被实例化一次,并且严格保证实例化顺序。静态内部类的属性是因为不用创建对象就可以调用里面的属性。
第一次加载Singleton类时不会去初始化INSTANCE,只有第一次调用getInstance时,虚拟机加载SingleHolder并初始化INSTANCE,这样不仅能确保线程安全,也能保证Singleton类的唯一性。
在类里面是保证不调用方法时不创建对象占用内存,因为调用时才会加载内部类。
public class Singleton { private Singleton(){ } //声明静态内部类 private static class SingletonHolder{ //声明并初始化对象,final是为了防止外部对他修改 private final static Singleton INSTANCE = new Singleton(); } //提供公共访问方式 public static Singleton getInstance(){ return SingletonHolder.INSTANCE; } }小结:推荐使用双重检查锁和静态内部类这两种方式的单例模式。在不考虑浪费空间的条件下,也可以选择饿汉式的枚举方式。
存在的问题
如果想要破坏上面的单例模式(枚举方式除外)的话即可以创建多个对象有两种方式,序列化和反射。
问题的解决
因为还没有学序列化和反射所以先空着
JDK源码中单例模式的应用
Runtime类



