Java设计模式-单例模式

单例模式(创建型)

意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

主要解决:一个全局使用的类频繁地创建与销毁。

定义: 

  • 单例类只能有一个实例。
  • 单例类必须自己创建自己的唯一实例。
  • 单例类必须给所有其他对象提供这一实例。

适用场景: 单例模式只允许创建一个对象,因此节省内存,加快对象访问速度,因此对象需要被公用的场合适合使用,如多个模块使用同一个数据源连接对象等等。如: 

  • 需要频繁实例化然后销毁的对象。 
  • 创建对象时耗时过多或者耗资源过多,但又经常用到的对象。 
  • 有状态的工具类对象。 
  • 频繁访问数据库或文件的对象。

在spring的依赖注入中,默认是单例注入。@Autowired修饰注入的对象都是单例对象。

五种类型:

  • 饿汉式(两种)
  • 懒汉式(三种)
  • 双重校验
  • 静态内部类
  • 枚举

一、不使用单例

不使用单例模式创建对象时候,创建的对象的hashcode都是不同,说明指向内存的地址是不用的。反之则为单例。

public class Singleton {
}
//new创建对象
Singleton singleton = new Singleton();
Singleton singleton1 = new Singleton();
if(singleton == singleton1){
    System.out.println(true);
}
System.out.println("new 对象1的hashcode:"+singleton.hashCode());
System.out.println("new 对象2的hashcode:"+singleton1.hashCode());

二、饿汉式(可以使用,但需要确认这个单例类会被使用到)

定义:通过静态方式创建实例,所以在jvm进行初始化时就会实例化对象,所以称为“饿汉式”,还没开始吃饭,碗上就被食堂阿姨装了饭。

缺点:当不使用这个单例类时,会造成内存浪费。

优点:通过静态方式初始化,只会初始化一次。

1、在静态常量实例化对象

public class Singleton01 {

    private final static Singleton01 INSTANCE = new Singleton01();

    private Singleton01() {
    }

    public static Singleton01 getInstance() {
        return INSTANCE;
    }
}
//饿汉式
Singleton01 singleton = Singleton01.getInstance();
Singleton01 singleton1 = Singleton01.getInstance();
if(singleton == singleton1){
    System.out.println(true);
    System.out.println("饿汉式1的对象1的hashcode:"+singleton.hashCode());
    System.out.println("饿汉式1的对象2的hashcode:"+singleton1.hashCode());
}

2、在静态代码块中实例化对象

public class Singleton02 {

    private static Singleton02 INSTANCE;


    private Singleton02() {
    }

    static {
        INSTANCE = new Singleton02();
    }

    public static Singleton02 getInstance() {
        return INSTANCE;
    }
}
//饿汉式
Singleton02 singleton = Singleton02.getInstance();
Singleton02 singleton1 = Singleton02.getInstance();
if (singleton == singleton1) {
    System.out.println(true);
    System.out.println("饿汉式2的对象1的hashcode:"+singleton.hashCode());
    System.out.println("饿汉式2的对象2的hashcode:"+singleton1.hashCode());
}

三、懒汉式(不推荐使用)

定义:在调用方法中实例化对象。自己饿了,需要吃饭,才自己去盛饭,所以称为“懒汉式”。

缺点:线程不安全。在多线程中可能会创建多个对象。

优点:在调用时才会进行实例化,不会造成内存浪费。

1、判断非null进行初始化(线程不安全,不推荐使用)

public class Singleton03 {

    private static Singleton03 INSTANCE;


    private Singleton03() {
    }

    public static Singleton03 getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new Singleton03();
        }
        return INSTANCE;
    }
}
//懒汉式
Singleton03 singleton = Singleton03.getInstance();
Singleton03 singleton1 = Singleton03.getInstance();
if (singleton == singleton1) {
    System.out.println(true);
    System.out.println("懒汉式1的对象1的hashcode:"+singleton.hashCode());
    System.out.println("懒汉式1的对象2的hashcode:"+singleton1.hashCode());
}

2、方法级别加锁(锁的重量太大,不推荐使用)

public class Singleton04 {

    private static Singleton04 INSTANCE;

    private Singleton04() {
    }

    public synchronized static Singleton04 getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new Singleton04();
        }
        return INSTANCE;
    }
}
//懒汉式
Singleton04 singleton = Singleton04.getInstance();
Singleton04 singleton1 = Singleton04.getInstance();
if (singleton == singleton1) {
    System.out.println(true);
    System.out.println("懒汉式2的对象1的hashcode:"+singleton.hashCode());
    System.out.println("懒汉式2的对象2的hashcode:"+singleton1.hashCode());
}

3、在代码块中加锁(线程不安全,不推荐使用,这样写的人有点掩耳盗铃,虽然锁住了代码块,但当两个线程通过了if判断在争抢锁,两个线程都会实例化对象,只是一个先一步实例,一个后一步实例,这就不是单例模式了)因为作者并没有使用多线程去实例化,所以这里还是判断还是为true。

public class Singleton05 {

    private static Singleton05 INSTANCE;

    private Singleton05() {
    }

    public static Singleton05 getInstance() {
        if (INSTANCE == null) {
            synchronized (Singleton05.class) {
                INSTANCE = new Singleton05();
            }
        }
        return INSTANCE;
    }
}
//懒汉式
Singleton05 singleton = Singleton05.getInstance();
Singleton05 singleton1 = Singleton05.getInstance();
if (singleton == singleton1) {
    System.out.println(true);
    System.out.println("懒汉式3的对象1的hashcode:"+singleton.hashCode());
    System.out.println("懒汉式3的对象2的hashcode:"+singleton1.hashCode());
}

四、双重校验(推荐使用)

定义:在调用方法中,进行双重非null判断。

优点:线程安全的。

public class Singleton06 {

    private static Singleton06 INSTANCE;

    private Singleton06() {
    }

    public static Singleton06 getInstance() {
        if (INSTANCE == null) {
            synchronized (Singleton06.class) {
                if (INSTANCE == null) {
                    INSTANCE = new Singleton06();
                }
            }
        }
        return INSTANCE;
    }
}
//双重校验
Singleton06 singleton = Singleton06.getInstance();
Singleton06 singleton1 = Singleton06.getInstance();
if (singleton == singleton1) {
    System.out.println(true);
    System.out.println("双重校验的对象1的hashcode:"+singleton.hashCode());
    System.out.println("双重校验的对象2的hashcode:"+singleton1.hashCode());
}

五、静态内部类(推荐使用)

定义:在调用方法中,再调用静态内部类去实例化对象。

优点:

  1. 在调用时才会进行实例化,不会造成内存浪费。
  2. 使用静态方式初始化,只会初始化,所以是线程安全的。
public class Singleton07 {

    private Singleton07() {
    }

    public static Singleton07 getInstance() {
        return Singleton.INSTANCE;
    }

    private static class Singleton {
        private static Singleton07 INSTANCE = new Singleton07();
    }
}
//静态内部类
Singleton07 singleton = Singleton07.getInstance();
Singleton07 singleton1 = Singleton07.getInstance();
if (singleton == singleton1) {
    System.out.println(true);
    System.out.println("静态内部类的对象1的hashcode:"+singleton.hashCode());
    System.out.println("静态内部类的对象2的hashcode:"+singleton1.hashCode());
}

六、枚举(jdk1.5之后的推荐使用)

定义:通过枚举的方式实例化对象

public enum Singleton08 {
    INSTANCE,
    INSTANCE01;
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
//枚举
Singleton08 singleton = Singleton08.INSTANCE;
Singleton08 singleton1 =  Singleton08.INSTANCE;
if(singleton == singleton1){
    System.out.println(true);
}
System.out.println("枚举类的对象1的hashcode:"+singleton.hashCode());
System.out.println("枚举类的对象1的hashcode:"+singleton1.hashCode());

ps:但是当我使用INSTANCE01这个对象时,内存地址指向是不用的。

//枚举
Singleton08 singleton = Singleton08.INSTANCE;
Singleton08 singleton1 =  Singleton08.INSTANCE01;
if(singleton == singleton1){
    System.out.println(true);
}
System.out.println("枚举类的对象1的hashcode:"+singleton.hashCode());
System.out.println("枚举类的对象2的hashcode:"+singleton1.hashCode());

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值