你能看懂的——单例模式

单例模式-base

什么是单例模式?

单例模式,即程序在运行的时候,总是只能生成一个实例。

实现思路

想要实现这一点,我们就不能让外部调用new关键字来创建对象。因此我们想到,私有化类的构造器

但是光是私有化类的构造器还没结束,我们还需要能够获得这个单个实例,因此我们需要在类上提供一个方法,用于获取这个实例,因此我们想到,提供一个方法来获取这个实例。

代码实现

由上面的推断,我们可以创建出一个单例模式的类,Singleton就是一个典型的单例模式的类。

/**
 * @author kerwin
 * @create 2024-05-08 17:21
 */
public class Singleton {
    private static Singleton singleton = new Singleton();

    private Singleton() {
        System.out.println("生成了一个实例。");
    }

    public static Singleton getInstance() {
        return singleton;
    }
}

我们在Main方法中进行调用,查看运行结果。

/**
 * @author kerwin
 * @create 2024-05-08 17:30
 */
public class Main {
    public static void main(String[] args) {
        System.out.println("Start.");
        Singleton obj1 = Singleton.getInstance();
        Singleton obj2 = Singleton.getInstance();
        if(obj1 == obj2){
            System.out.println("obj1和obj2是相同的实例");
        }else{
            System.out.println("obj1和obj2是不同的实例");
        }
        System.out.println("End.");
    }
}

拿着代码跑一下吧!!

运行结果如下
在这里插入图片描述
由上面的代码运行结果可以得出,创建的两个对象确实是同一个对象。

恭喜你!到这里,你已经掌握了单例模式的最基础的知识!怎么样?简单吧!接下来进阶学习一下!!

单例模式-plus

练习题一

在下面的TicketMaker类中,每次调用getNextTicketNumber方法都会返回1000,1001,1002…的数列。我们可以用它生成票的编号或是其他序列号。但在现在该类的实现方式下,我们可以生成多个该类的实例,导致生成的票编号无法保持唯一且连续,请修改代码,运用Singleton 模式确保只能生成一个该类的实例。

public class TicketMaker{
	private int ticket = 1000;
	public int getNextTicketNumber(){
		return ticket++; 
	}
}

练习题二

请编写Triple类,实现最多只能生成3个Triple类的实例,实例编号分别为0,1,2且可以通过getInstance(int id)来获取该编号对应的实例

练习题三

某位开发人员编写了如下的singleton 类。但这并非严格的 Singleton模式。请问是为什么呢?

public class Singleton {
    private static Singleton singleton = null;
    private Singleton(){
        System.out.println("生成了一个实例。");
    }

    public static Singleton getInstance(){
        if(singleton==null){
            singleton = new Singleton();
        }
        return singleton;
    }
}

答案

习题一

思路:我们只需要把TicketMaker类的构造器私有化,再将我们唯一的实例暴露出去就好啦,这是一个经典的单例模式的使用场景。
实现的TicketMaker类如下

public class TicketMaker {
    private int ticket = 1000;

    private static TicketMaker singleton = new TicketMaker();

    private TicketMaker() {

    }

    public static TicketMaker getInstance(){
        return singleton;
    }

    public synchronized int getNextTicketNumber() {
        return ticket++;
    }
}

让我们来用一下吧!~

public class Main1 {
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            System.out.println(TicketMaker.getInstance().getNextTicketNumber());
        }
    }
}

输出结果

1000
1001
1002
1003
1004
1005
1006
1007
1008
1009

习题二

思路:我们可以使用数组或集合来创建多个Triple类的实例,在需要的时候,根据id使用数组或集合下标来进行获取。

public class Triple {

    private static List<Triple> list = Arrays.asList(new Triple(),new Triple(),new Triple());


    private Triple() {

    }

    public static Triple getInstance(int id) {
        return list.get(id);
    }
}

调用一下试试看!

public class Main {
    public static void main(String[] args) {
        Triple instance0_1 = Triple.getInstance(0);
        Triple instance0_2 =  Triple.getInstance(0);
        if(instance0_1==instance0_2){
            System.out.println("相同");
        }
        Triple instance1_1 = Triple.getInstance(1);
        Triple instance1_2 =  Triple.getInstance(1);
        if(instance1_1==instance1_2){
            System.out.println("相同");
        }
    }
}

习题三

思路:这不是一个严格的单例模式,是因为在调用getInstance方法的时候,在多线程几乎同时调用的情况下,则可能会创建不只一个对象,导致不满足单例模式永远只有一个对象的基本原则。

让我们仔细的探索一下吧!

我们先改进一下Singleton类,我们在调用getInstance方法的时候进行延时,确保可以多个线程都可以在实例singleton开始为null的时候进入getInstance方法,从而多次调用new来创建多个对象。

public class Singleton {
    private static Singleton singleton = null;

    private Singleton() {
        //减慢创建实例的速度
        slowDown();
        System.out.println("生成了一个实例");
    }

    public static Singleton getInstance() {
        if(singleton==null){
            singleton = new Singleton();
        }
        return singleton;

    }

    public static void slowDown() {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

我们创建自己的线程类

public class MyThread extends Thread{

    public MyThread(String name) {
        super(name);
    }
    public void run(){
        Singleton obj = Singleton.getInstance();
        System.out.println(getName()+": obj = "+obj);
    }
}

在Main主类中进行调用

public class Main3{
    public static void main(String[] args) {
        System.out.println("Start.");

        new MyThread("A").start();
        new MyThread("B").start();
        new MyThread("C").start();

        System.out.println("end");
    }
}

结果如下

Start.
end
生成了一个实例
生成了一个实例
生成了一个实例
B: obj = singleton.plus.Singleton@2591fc33
C: obj = singleton.plus.Singleton@50f6a567
A: obj = singleton.plus.Singleton@2bc3709c

我们通过延时,模拟了多个线程几乎同时进入到getInstance,结果是出现了多个不同的实例,由此可以见得,这种单例方式非严格的单例模式。

单例模式的讲解到此就结束啦,感谢阅读。💕

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值