作用
- 单例模式保证了 系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,单例模式可以提高系统性能。
- 当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用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-尚硅谷,转载请注明出处。**