java基础_设计模式_单例模式

刚刚接触单例模式的我们,在阅读大神写的代码的时候,有时很想不通为什么这里要使用单例这种模式,有啥好处吗,不这样写又会咋滴等问题的困扰。下面我就想比较通俗的、用自己的语言组织讲解一下单例模式,要是有地方理解不到位或出现偏差,希望大家能及时指出。


1.什么是单例模式?


2.为什么会有这种需求,在哪些地方用单例模式,原因或者好处是什么?


3.如何创建单例模式?常见的创建方式优缺点。


4.单列和工具类很像,比如math类是这样使用的Math.double(2,8);有啥区别呢?


1.单例模式:

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

通常我们可以让一个全局变量使得一个对象被访问,但它不能防止你实例化多个对象。一个最好的办法就是,让类自身负责保存它的唯一实例。这个类可以保证没有其他实例可以被创建,并且它可以提供一个访问该实例的方法。


简单的讲就是:整个项目,有且只能创建唯一一个这个类的实例。 


2.为啥会有这样的需求呢?举个例子在电脑任务栏右键然后点击启动任务管理器,会弹出一个任务管理器窗口,然后你再重复这样的操作,看能再弹出一个任务管理器的窗口吗?肯定不能!要是谁能,请私信我。哈哈哈。。。这里对弹出窗口唯一限制用的就是单列模式。有且只能创建唯一一个实例即确保对象的唯一性。

为了确保对象的唯一性我们可以使用单列,但是为什么要确保对象的唯一性?

我认为好处有两点:

①省资源。项目中大量使用一个类,比如网络请求框架,要是每次使用创建对象都new的话,很浪费资源的。

比如我封装的网络框架使用时是这样的:ApiService.getInstance().getData().enqueue(//请求回调操作);

②就像弹出任务管理器一样要统一,只能有一个。


3.创建单列的方式:

最简单的就是根据单列的概念去创建。即有且只有一个对象。①私有的构造方法,不允许外部代码通过new的方式创建对象。②获取对象的方法中加上是否已经存在的判断,有则直接返回,没有则new一个。

如下:

/**
 * 单利模式总结
 * 简单的讲单列要达到什么效果?整个项目中 有且只能产生一个类的实例
 * 我认为使用单列模式原因主要有以下两点
 * 1.减少内存的占用 要是在每一次使用的时候都new一个对象,相当占用资源
 * 2.有些情况适合使用单列 比如桌面上弹出任务管理器窗口 是只能弹出一个的
 * 3.如何使用单列?
 */

public final class Singleton {

    private static Singleton singleton;

    //1.私有的构造 要是修饰public或者不写的话 类创建对象的时候直接走默认的构造 private意味着对于外部代码而言不能通过new来实例化 但是类内部可以的
    private Singleton() {

    }

    //2.通过Singleton.getInstance() 获取唯一实例 加上判断 存在则返回不存在则直接本类中new 这是最简单的创建单例方式
    public static Singleton getInstance() {

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

}

测试类:

/**
 * 测试单例
 */

public class TestSingleTon {
    public static void main(String[] args) {

        //        Singleton singleton = new Singleton();  提示错误:私有的构造不允许new
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();

        if (instance1 == instance2) {
            System.out.println("说明是单例模式,只能创建一个实例");
        }
    }
}
说明创建了唯一的一个实例。

但是存在一个问题就是,当有多个线程并行调用getInstance()的时候,就会创建多个单列。也就是说在多线程下不能正常工作。最直接的方法就是加锁,只能一个一个的来,代码如下:

  public static Singleton getInstance() {.... }  改为  public synchronizeed  static Singleton getInstance() {...}

这样保证了同一时间只能有一个线程调用getInstance()方法。但是效率极低,很少有情况会使用同步啊。(关于同步和异步的概念,在文章末尾有简单的解释)

为了保证多线程访问我们一般会双重加锁:

双重检查锁:double-checked-locking   改变了3,4,5行代码

public final class Singleton {

    private static Singleton singleton;

    //1.私有的构造  要是修饰public或者不写的话 类创建对象的时候直接走默认的构造 private意味着对于外部代码而言不能通过new来实例化 但是类内部可以的
    private Singleton() {

    }

    //2.通过Singleton.getInstance() 获取唯一实例 加上判断 存在则返回不存在则直接本类中new  这是最简单的创建单例方式
    public synchronized static Singleton getInstance() {

        if (singleton == null) {                    //3.多个线程一起进入同步代码块的if
            synchronized (Singleton.class) {        //4.修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
                if (singleton == null) {            //5.保证进入一个 判断是不是有了 有则返回
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }

}

这样就解决了多线程访问单列的问题。


4.对比工具类:

给大家放一个常用的Math类的源代码:

以下是Math类:
public final class Math {

    /**
     * Don't let anyone instantiate this class.
     */
    private Math() {}

    public static native double sin(double a);

}
简单分析:

①私有的构造不允许外部代码以new的方式创建对象

②声明为final关键字不允许被其他类继承

③提供native修饰的静态方法,方法本身没有实现,意味着这样的计算是使用jni的方式,预计c的计算效率高。
     注:Native Method 就是一个java调用非java代码的接口。该方法没有具体的java语言实现,比如又可能是c。

④自己书写工具类的注意final关键字等这样的细节,尽量规范。

⑤哇塞,突然感觉好心酸啊。工具类竟然是一个不能new对象、不能有子类(final),得多么的寂寞。


以上就是自己对单例模式的理解,有问题请大家积极指出。


PS:线程同步和异步的概念

什么时候必须同步?什么叫同步?如何同步?

只要在几个线程之间共享变量,就必须使用synchronized同步(或者volatite易变的)确保一个线程可以看见另一个线程做的更改。一个线程所做的变化何时以及如何变成对其他线程可见。

同步:共享的资源在同一时刻只能被一个线程使用,这种方式成为同步。

为了防止多个线程并发对同一数据的修改,所以需要同步,否则会造成数据不一致,也就是所谓的线程安全。

同步:提交请求=》等待服务器处理=》处理完返回  这个期间客户端浏览器不能干任何事
异步:请求通过事件触发=》服务器处理(此时浏览器依然可以做其他事情)=》处理完毕。

简单的讲同步即有顺序
异步:大家一起上公家车,没有秩序,可以同时发生。

很多时候我们使用的是异步多线程来处理同一业务里的大量数据,好比一万个订单要处理,如果你使用一个线程顺序执行,一个个处理,非常耗时间;但是多线程就可以开100个线程异步处理,这样效率提高很多。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值