JAVA项目中什么情况用单例模式【单例模式一】

前言

相信,尽管作为一个入门开发者,即使你没有写过单例,你也一定听过单例模式,在此之前,为准备面试,我也看过不少单例的文章,就是你常能收到的那种《五种单例模式写法》等等,诸如此类,但一直没有深入的理解过单例模式,今天趁此机会,想沉下心来,好好研究研究这个东西,当然即使现在你不是面试,你也应该搞懂这个东西,因为,首先,他不难,他也许是最容易搞懂的设计模式了,正题!

观看本文技巧,重点关注加粗和标黄地方是我自己认为比较重点核心的知识点,如果实在没耐心,一定收藏【反正我也知道你收藏了也不一定看 - . - 】

什么是单例

单例模式是一种常用的软件设计模式,所谓单例就是所有的请求都用一个对象来处理,比如我们常用的service和dao层的对象通常都是单例的,而多例则指每个请求用一个新的对象来处理,比如action;

通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而达到使用目的,同时还能方便对实例个数的控制并节约系统资源。如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。

一、静态方法,非静态方法常驻内存?

一般都认为是这样,并且怕静态方法占用过多内存而建议使用非静态方法,其实这个理解是错误的。静态方法和非静态方法,他们都是在第一次加载后就常驻内存,所以方法本身在内存里,没有什么区别。

二、下静态方法和非静态方法的区别

在内存中的区别是:

  • 非静态方法在创建实例对象时,因为属性的值对于每个对象都各不相同,因此在new一个实例时,会把这个实例属性在GC Heap(堆内存)里拷贝一份,同时这个new出来的对象的引用放在堆栈上,堆栈指针指向了刚才拷贝的那一份实例的内存地址上。

  • 静态方法则不需要,因为静态方法里面的静态字段,就是保存在Method Table(方法区)里了,只有一份

因此静态方法和非静态方法,在调用速度上,静态方法速度一定会快点,因为非静态方法需要实例化,分配内存,但静态方法不用,但是这种速度上差异可以忽略不计。

三、为什么要有非静态方法?

早期的结构化编程,几乎所有的方法都是“静态方法”,引入实例化方法概念是面向对象概念出现以后的事情了,区分静态方法和实例化方法不能单单从性能上去理解,创建c++,java,c#这样面向对象语言的大师引入实例化方法一定不是要解决什么性能、内存的问题,而是为了让开发更加模式化、面向对象化。这样说的话,静态方法和实例化方式的区分是为了解决模式的问题。

接下来继续思考,如果我们全部用静态方法,不用非静态方法,不是一样能实现功能吗?是的,没错,但是你的代码是基于对象,而不是面向对象的,因为面向对象的继承和多态,都是非静态方法。

第二个原因是为什么不建议都用静态方法,我们如果多线程的情况下,如果静态方法使用了一个静态字段,这个静态字段可以会被多个线程修改,因此说如果在静态方法里使用了静态变量,这就会有线程安全问题,当然了,就算不是多线程,因为静态字段只有一份,同样会有被其他地方修改的问题。


从这三点我们得出的结论如下:

一、 什么时候用静态方法,什么时候使用非静态方法?

既然静态方法和实例化方式的区分是为了解决模式的问题,如果我们考虑不需要继承和多态的时候,就可以使用静态方法,但就算不考虑继承和多态,就一概使用静态方法也不是好的编程思想。

从另一个角度考虑,如果一个方法和他所在类的实例对象无关,那么它就应该是静态的,否则就应该是非静态。因此像工具类,一般都是静态的

二、 为什么使用单例模式而不用静态方法?

从面相对象的角度讲:

虽然都能实现目的,但是他们一个是基于对象,一个是面向对象的,就像我们不面相对象也能解决问题一样,面相对象的代码提供一个更好的编程思想。

如果一个方法和他所在类的实例对象无关,那么它就应该是静态的,反之他就应该是非静态的。如果我们确实应该使用非静态的方法,但是在创建类时又确实只需要维护一份实例时,就需要用单例模式了。

比如说我们在系统运行时候,就需要加载一些配置和属性,这些配置和属性是一定存在了,又是公共的,同时需要在整个生命周期中都存在,所以只需要一份就行,这个时候如果需要我再需要的时候new一个,再给他分配值,显然是浪费内存并且再赋值没什么意义,所以这个时候我们就需要单例模式或静态方法去维持一份且仅这一份拷贝,但此时这些配置和属性又是通过面向对象的编码方式得到的,我们就应该使用单例模式,或者不是面向对象的,但他本身的属性应该是面对对象的,我们使用静态方法虽然能同样解决问题,但是最好的解决方案也应该是使用单例模式。

从功能上讲:单例模式可以控制单例数量;可以进行有意义的派生;对实例的创建有更自由的控制;

三、其他:

数据库连接能不能做SingleTon?

如果是简单地把一个connection对象封存在单例对象中,这样是错误的,因此连接池里有多个链接可以用,如果使用SingleTon,那在WEB访问时,就只能用一个数据库链接,那不是死的很惨?

但是连接池可以使用单例模式,初始化的时候创建譬如100个connection对象,然后再需要的时候提供一个,用过之后返回到pool中,我们用单例模式,是保证连接池有且只有一个。

增强理解

比如一个系统的任务管理器应该只有一个,不管你打开多少个任务管理器窗口,操作的都是同一个任务列表。如果每次打开一个窗口就弹出一个新的任务管理器的话那就乱套了。

Spring中的bean就是单例的,Servlet也是单例的。

但是说单例模式严格来讲不太合适,因为单例要求保证一个类只有一个实例,bean和servlet都是可以自己去new的。如果直接使用autowired去注入或者注册Servlet的话,使用的都是同一个实例。

下篇更新烂大街的各种单例模式写法

参考:

http://www.sohu.com/a/231184251_467792 为什么要用单例
https://blog.csdn.net/qq_35661171/article/details/83180546 spring中的bean默认单例模式
https://www.jianshu.com/p/ef344305fc3b
https://segmentfault.com/a/1190000016763039 spring单例 与多线程矛盾

  • 10
    点赞
  • 69
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值