作用
- 单例模式保证了 系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,单例模式可以提高系统性能。
- 当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用new。
使用场景:
1、创建和销毁频繁。
2、对象过大,重量级,但是常用,频繁访问-工具类、数据源、session工厂等。
饿汉式(静态变量):
原理: 1、私有化构造器(防止new) 2、内部属性创建对象,随类而生(唯一性) 3、提供公有静态方法,返回对象(外部可使用) 优点:写法简单,类装载时期完成实例化,避免线程同步问题 缺点:不可懒加载,除非保证系统中会使用到,否则造成资源浪费 总结:可用,但是有可能造成内存浪费。
public class Hungre1 { public static void main(String[] args) { // 除去getInstance()还有其他装载方式可能浪费内存 Singleton singleton = Singleton.getInstance(); Singleton singleton2 = Singleton.getInstance(); System.out.println(singleton.equals(singleton2)); } } /** * (静态常量) */ class Singleton { /** * 1、构造器私有化,外部无法new */ private Singleton() { } /** * 2、本类内部创建对象实例 */ private final static Singleton INSTANCE = new Singleton(); /** * 3、对外提供一个公有的静态方法,返回实例对象 */ public static Singleton getInstance() { return INSTANCE; } }
饿汉式(静态代码块):
原理:将类中创建操作放置在static{}中,其余同上。
public class Hungre2 { public static void main(String[] args) { // 随类加载创建,可能内存浪费 Singleton2 singleton = Singleton2.getInstance(); Singleton2 singleton2 = Singleton2.getInstance(); System.out.println(singleton.equals(singleton2)); } } /** * (静态变量) */ class Singleton2 { static { instance = new Singleton2(); } /** * 1、构造器私有化,外部无法new */ private Singleton2() {} /** * 2、本类内部创建对象实例 */ private final static Singleton2 instance; /** * 3、对外提供一个公有的静态方法,返回实例对象 */ public static Singleton2 getInstance() { return instance; } }
懒汉式(线程不安全):
原理: 1、私有化构造器(防止new) 2、提供公有静态方法创建并返回对象(外部可使用) 3、在创建方法中确保对象只有一个(唯一性),但无法保证线程安全问题 优点:懒加载,解决资源浪费问题。 缺点:多线程模式下可能产生多个实例。 总结:不安全,不建议使用。
public class Lazy1 { public static void main(String[] args) { Singleton3 singleton = Singleton3.getInstance(); Singleton3 singleton2 = Singleton3.getInstance(); System.out.println(singleton.equals(singleton2)); } } class Singleton3 { private static Singleton3 instance; /** * 私有化构造器 */ private Singleton3() {} /** * 提供一个静态的公有方法,当使用到该方法时,才来创建对象。 * @return 实例对象 */ public static Singleton3 getInstance() { if (null == instance) { instance = new Singleton3(); } return instance; } }
懒汉式(线程安全,同步方法):
原理: 1、私有化构造器(防止new) 2、提供公有静态方法创建并返回对象(外部可使用) 3、在创建方法中确保对象只有一个(唯一性),synchronized锁方法解决同步安全问题 优点:解决线程安全问题。 缺点:锁方法导致性能问题,每次调用创建方法都会加锁,包括已经实力化对象后。 总结:性能低,不建议使用。
public class Lazy3 { public static void main(String[] args) { Singleton3 singleton = Singleton3.getInstance(); Singleton3 singleton2 = Singleton3.getInstance(); System.out.println(singleton.equals(singleton2)); } } class Singleton5 { private static Singleton5 instance; /** * 私有化构造器 */ private Singleton5() {} /** * 提供一个静态的公有方法,当使用到该方法时,才来创建对象。 * @return 实例对象 */ public static synchronized Singleton5 getInstance() { if (null == instance) { instance = new Singleton5(); } return instance; } }
双重检查:
原理: 1、私有化构造器(防止new) 2、提供公有静态方法,加入双重检测(外部可使用、唯一性) 优点:解决了线程安全(双重检测)、懒加载(get方法)问题。 总结:推荐使用。
public class DoubleCheck { public static void main(String[] args) { Singleton6 singleton = Singleton6.getInstance(); Singleton6 singleton2 = Singleton6.getInstance(); System.out.println(singleton.equals(singleton2)); } } class Singleton6 { private Singleton6() {} /** * volatile 全局可见 */ private static volatile Singleton6 singleton6; public static Singleton6 getInstance() { if (singleton6 == null) { synchronized (Singleton6.class) { if (singleton6 == null) { singleton6 = new Singleton6(); } } } return singleton6; } }
静态内部类:
原理: 1、私有化构造器(防止new) 2、静态内部类创建方法(懒加载) 3、提供公有静态方法调用静态内部类(外部可使用) 4、静态属性只会在第一次加载类时初始化,该唯一性由JVM保证(唯一性) 优点:避免线程不安全、静态内部类实现延迟加载、效率高。 总结:推荐使用。
public class StaticInside { public static void main(String[] args) { Singleton7 singleton1 = Singleton7.getInstance(); Singleton7 singleton2 = Singleton7.getInstance(); System.out.println(singleton1.equals(singleton2)); } } class Singleton7 { // 构造器私有化 private Singleton7() {} // 静态内部类,需要一个静态属性 外部类加载时不会被加载 private static class SingletonInstance { private static final Singleton7 INSTANCE = new Singleton7(); } public static Singleton7 getInstance() { return SingletonInstance.INSTANCE; } }
枚举:
原理: 1、枚举反编译后:public static final 枚举类 ENUM_CLASS;(final修饰的对象地址值不变,确保只有一个) 2、私有化构造器(防止new) 优点:简单快捷安全高效。 总结:推荐使用。 - Josh Bloch(Effective Java作者)。
public class Enum { public static void main(String[] args) { Singleton8 instance1 = Singleton8.INSTANCE; Singleton8 instance2 = Singleton8.INSTANCE; System.out.println(instance1.equals(instance2)); } } enum Singleton8 { INSTANCE; }
注:文章内容学习自bilibili-尚硅谷,转载请注明出处。