单例模式1.0

发布时间 2023-09-06 19:12:39作者: 千叶落木

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

@

一、单例模式是什么?

1.官方解释

单例模式,属于创建类型的一种常用的软件设计模式。通过单例模式的方法创建的类在当前进程中只有一个实例(根据需要,也有可能一个线程中属于单例,如:仅线程上下文内使用同一个实例)
——百度百科

2.个人理解

一种最简单的设计模式,目标:只创建一次对象,在程序中其它需要用到相同功能的地方,只调用这个对象而不创建新的对象,保证了数据的一致性(例如:Java中的日历类)

3.分类

1.饿汉式:

饿汉式:在程序加载时就创建好实例,等待被调用。
优点:采用以空间换时间的策略。由于创建对象是一个比较慢的过程,提前创建好实例(对象)可以大大节省程序执行的时间。
缺点:如果不使用这个实例会浪费一定的空间资源(反驳:1.一般被设计成单例模式的对象不使用的可能性不大;2.如今硬件存储容量较大,对性能方面的需求:时间>空间)

2.懒汉式:

懒汉式:在程序调用时才创建实例。
优点:不使用就不创建节省内存空间。
缺点:拖慢程序运行速度。

二、详细说明

下面以一个简单的Java代码例子对设计过程进行分解解释。

目标:只创建一次对象

1.饿汉式

准备工作
创建Dog类(单例模式)和Test测试类
Dog类代码如下(示例):

//Dog类
public class Dog {
    public Dog() {
    }
}

Test类代码如下(示例):

//Test类
public class Test {
    public static void main(String[] args) {
        Dog d1=new Dog();
        Dog d2=new Dog();
        System.out.println(d1);
        System.out.println(d2);
        Dog d3=d2;
        System.out.println(d3);
    }
}

打印地址:相同表示是同一对象,不相同表示不是同一对象。
运行结果
运行结果
问题:new出来的两个对象地址不相同(表是不是同一个对象),不满足要求。
尝试解决:将public Dog()改为:private Dog()
Dog类代码如下(示例):

//Dog类
public class Dog {
    private Dog() {
    }
}

Test类

新的问题:Test类里面不能再new Dog();
尝试解决:将Test类里面的new Dog();放入Dog类;
Dog类代码如下(示例):

//Dog类
public class Dog {
private Dog(){      
    }
    public  Dog getDog(){  
        Dog dog=new Dog();
        return dog;
    }
}

Test类代码如下(示例):

//Test类
public class Test {
    public static void main(String[] args) {
        Dog d1=Dog.getDog();
        System.out.println(d1);
    }
}

Test类
新的问题:没有暴露外部访问(Test类不能访问Dog类)
尝试解决:将Dog类里面的public Dog getDog()设为静态便于访问;
Dog类代码如下(示例):

//Dog类
public class Dog {
private Dog(){    
    }
    public static Dog getDog(){
        Dog dog=new Dog();
        return dog;
    }
}

解决!
再次测试
Test类代码如下(示例):

//Test类
public class Test {
    public static void main(String[] args) {
        Dog dog1 = Dog.getDog();
        Dog dog2 = Dog.getDog();
        System.out.println(dog1);
        System.out.println(dog2);
    }
}

测试结果
新的问题:地址不一致(对象不是同一个)
尝试解决:将“Dog dog=new Dog();”写到getDog(){}外面
Dog类

Dog类代码如下(示例):

//Dog类
public class Dog {
    public Dog dog=new Dog();
    private Dog(){

    }
    public static Dog getDog(){

        return dog;
    }
}

新的问题:需要一个静态返回值,但是返回值不是静态
Dog类
尝试解决:将“Dog dog=new Dog();”设置为静态;
Dog类代码如下(示例):

//Dog类
public class Dog {
    public   static Dog dog=new Dog();
    private Dog(){

    }
    public static Dog getDog(){

        return dog;
    }
}

解决!
测试

Test类代码如下(示例):

//Test类
public class Test {
    public static void main(String[] args) {
        Dog dog1 = Dog.getDog();
        Dog dog2 = Dog.getDog();
        System.out.println(dog1);
        System.out.println(dog2);
    }
}

结果:
测试结果
嗯~貌似实现了目标
再次测试
Test类代码如下(示例):

//Test类
public class Test {
    public static void main(String[] args) {
        Dog dog1 = Dog.getDog();
        Dog.dog = null;
        Dog dog2 = Dog.getDog();
        System.out.println(dog1);
        System.out.println(dog2);
        Dog dog3 = Dog.getDog();
        System.out.println(dog3);
    }
}

结果
测试结果
新的问题:Dog类里面“public static Dog dog=new Dog();”
public导致Test类里面可以改变dog
尝试解决:将public改为private
Dog类代码如下(示例):

//Dog类
public class Dog {
    private    static Dog dog=new Dog();
    private Dog(){

    }
    public static Dog getDog(){

        return dog;
    }
}

结果:Test类中不能再“ Dog.dog = null;”
失效
再次测试
Test类代码如下(示例):

//Test类
public class Test {
    public static void main(String[] args) {
        Dog dog1 = Dog.getDog();

        Dog dog2 = Dog.getDog();
        System.out.println(dog1);
        System.out.println(dog2);
        Dog dog3 = Dog.getDog();
        System.out.println(dog3);
    }
}

结果
在这里插入图片描述
解决!
新的测试:尝试将上述Test类里面的问题(“Dog.dog = null;”)加入Dog类
Dog类代码如下(示例):

//Dog类
public class Dog {
    private    static Dog dog=new Dog();
    private Dog(){

    }
    public static Dog getDog(){
        Dog.dog = null;
        return dog;
    }
}

测试
Test类代码如下(示例):

//Test类
public class Test {
    public static void main(String[] args) {
        Dog dog1 = Dog.getDog();

        Dog dog2 = Dog.getDog();
        System.out.println(dog1);
        System.out.println(dog2);
        Dog dog3 = Dog.getDog();
        System.out.println(dog3);
    }
}

结果
测试
新的问题:在Dog类里面仍然可以改变dog
尝试解决:在“private static Dog dog=new Dog();”里面加final ,表示最后的不能修改的
结果:该行代码失效Dog
去掉该行代码!
Dog类代码如下(示例):

//Dog类
public class Dog {
    private  final   static Dog dog=new Dog();
    private Dog(){

    }
    public static Dog getDog(){
 
        return dog;
    }
}

再次测试
Test类代码如下(示例):

//Test类
public class Test {
    public static void main(String[] args) {
        Dog dog1 = Dog.getDog();

        Dog dog2 = Dog.getDog();
        System.out.println(dog1);
        System.out.println(dog2);
        Dog dog3 = Dog.getDog();
        System.out.println(dog3);
    }
}

结果
测试
完美解决!
以上Dog类就是一个完整的饿汉式单例模式!
总结:饿汉式单例模式代码如下

//Dog类
public class Dog {
    private  final   static Dog dog=new Dog();
    private Dog(){

    }
    public static Dog getDog(){

        return dog;
    }
}

2.懒汉式

问题:饿汉式提前创建对象在不使用对象时会浪费内存空间
尝试解决:使用的时候再创建(调用时判断,如果没有再创建)
改造Dog类
Dog类代码如下(示例):

//Dog类
public class Dog {
    private  final   static Dog dog=null;
    private Dog(){

    }
    public static Dog getDog(){
        dog=new Dog();
        return dog;
    }
}

这里会报错:原因是“final”与“ dog=new Dog();”冲突
修改Dog,删除final
Dog类代码如下(示例):

//Dog类
public class Dog {
    private  static Dog dog=null;
    private Dog(){

    }
    public static Dog getDog(){
        dog=new Dog();
        return dog;
    }
}

测试:
Test类代码如下(示例):

//Test类
public class Test {
    public static void main(String[] args) {
        Dog dog1 = Dog.getDog();

        Dog dog2 = Dog.getDog();
        System.out.println(dog1);
        System.out.println(dog2);
        Dog dog3 = Dog.getDog();
        System.out.println(dog3);
    }
}

结果
测试
貌似实现了目标
Dog类
潜在问题
涉及线程调度时第一个线程(Thread1)进来执行到第11行代码结束时间片刚好用完,但是还没有执行第14行,于是在第11行与第14行代码之间等待;此时线程2(Thread2)进来执行,相同的原因停留在在第11行与第14行代码之间等待;之后线程1(Thread1)重新获得时间片执行后续代码;而线程2(Thread2)苏醒时会再执行一次后续代码,从而导致不符合单例要求。
就上述问题换一种场景(多线程)再次测试
Test类代码如下(示例):

//Test类(多线程)
public class Test2 {
    public static void main(String[] args) {
        Thread thread = new Thread() {
            public void run() {
                Dog dog = Dog.getDog();
                int hashCode = dog.hashCode();
                System.out.println(Thread.currentThread().getName() + ":" + hashCode);
            }
        };
        thread.start();
        Thread thread2 = new Thread() {
            public void run() {
                Dog dog = Dog.getDog();
                int hashCode = dog.hashCode();
                System.out.println(Thread.currentThread().getName() + ":" + hashCode);
            }
        };
        thread2.start();
    }
}

结果
多线程测试
新的问题:可以看到两个线程所创建的对象的hashCode值不一样,那么也就代表如果多线程调用该单例模式则会出现线程安全问题。
尝试解决:加个“synchronized”在线程1(Thread1)进来后锁定,保证线程2(Thread2)不能进入
Dog类代码如下(示例):

//Dog类
public class Dog {
    private  static Dog dog=null;
    private Dog(){

    }
    public static synchronized Dog getDog(){
        if(dog==null){
            dog=new Dog();
        }
        return dog;
    }
}

再次测试
Test类代码如下(示例):

//Test类(多线程)
public class Test2 {
    public static void main(String[] args) {
        Thread thread = new Thread() {
            public void run() {
                Dog dog = Dog.getDog();
                int hashCode = dog.hashCode();
                System.out.println(Thread.currentThread().getName() + ":" + hashCode);
            }
        };
        thread.start();
        Thread thread2 = new Thread() {
            public void run() {
                Dog dog = Dog.getDog();
                int hashCode = dog.hashCode();
                System.out.println(Thread.currentThread().getName() + ":" + hashCode);
            }
        };
        thread2.start();
    }
}

结果
结果
解决!
以上Dog类就是一个完整的懒汉式单例模式!
总结:懒汉式单例模式代码如下

//Dog类
public class Dog {
    private  static Dog dog=null;
    private Dog(){

    }
    public static synchronized Dog getDog(){
        if(dog==null){
            dog=new Dog();
        }
        return dog;
    }
}

详细学习请参考:

https://blog.csdn.net/qq_41286145/article/details/107357604?ops_request_misc={"request_id"%3A"169320979416800215034538"%2C"scm"%3A"20140713.130102334.."}&request_id=169320979416800215034538&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2blogtop_positive~default-2-107357604-null-null.268%5Ev1%5Ekoosearch&utm_term=%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F&spm=1018.2226.3001.4450