静态⽅法与单例模式的区别以及为什么要⽤单例模式⽽不是静态⽅法
单例模式的几种实现方式我们在编程中最常⽤的模式就是单例模式了,然⽽单例模式都⽤在什么场合?为什么不⽤静态⽅法⽽要⽤单例模式呢?要搞清这些问题,需要从静态⽅法和⾮静态⽅法的区别和联系说起。
⼀、静态⽅法常驻内存,⾮静态⽅法只有使⽤的时候才分配内存?
⼀般都认为是这样,并且怕静态⽅法占⽤过多内存⽽建议使⽤⾮静态⽅法,其实这个理解是错误的。
为什么会这样,先从内存分配开始说起:
托管堆的定义:对于32位的应⽤程序来说,应⽤程序完成进程初始化后,CLR将在进程的可⽤地址空间分配⼀块保留的地址空间,它是进程(每个进程可使⽤4GB)中可⽤地址空间上的⼀块内存区域,但并不对应任何物理内存,这块地址空间即是托管堆。
托管堆有分为多个区域,其中最重要的是垃圾回收堆(GC Heap)和加载堆(Loader Heap),GC
Heap⽤于存储对象实例,受GC管理;Loader Heap⼜分为High-Frequency Heap、Low-Frequency
Heap和Stub Heap,不同的堆上⼜存储不同的信息。Loader
Heap最重要的信息就是元数据相关的信息,也就是Type对象,每个Type在Loader Heap上体现为⼀个Method
Table(⽅法表),⽽Method Table中则记录了存储的元数据信息,例如基类型、静态字段、实现的接⼝、所有的⽅法等等。Loader Heap不受GC控制,其⽣命周期为从创建到AppDomain卸载。(摘⾃《》)
由此我们就明⽩了,静态⽅法和⾮静态⽅法,在内存⾥其实都放在Method Table⾥了,在⼀个类第⼀次被加载的时候,它会在Loader
Heap⾥把静态⽅法,⾮静态⽅法都写⼊Method Table中,⽽且Loader
Heap不受GC控制,所以⼀旦加载,GC就不会回收,直到AppDomain卸载
由此我们也明⽩了,静态⽅法和⾮静态⽅法,他们都是在第⼀次加载后就常驻内存,所以⽅法本⾝在内存⾥,没有什么区别,所以也就不存在”静态⽅法常驻内存,⾮静态⽅法只有使⽤的时候才分配内存“这个结论了。
⼆、静态⽅法和⾮静态⽅法的区别?
在内存中的区别是,⾮静态⽅法在创建实例对象时,因为属性的值对于每个对象都各不相同,因此在new⼀个实例时,会把这个实例属性在GC
Heap⾥拷贝⼀份,同时这个new出来的对象放在堆栈上,堆栈指针指向了刚才拷贝的那⼀份实例的内存地址上。⽽静态⽅法则不需要,因为静态⽅法⾥⾯的静态字段,就是保存在Method
Table⾥了,只有⼀份。
因此静态⽅法和⾮静态⽅法,在调⽤速度上,静态⽅法速度⼀定会快点,因为⾮静态⽅法需要实例化,分配内存,但静态⽅法不⽤,但是这种速度上差异可以忽略不计。
三、为什么要有⾮静态⽅法?
早期的结构化编程,⼏乎所有的⽅法都是“静态⽅法”,引⼊实例化⽅法概念是⾯向对象概念出现以后的事情了,区分静态⽅法和实例化⽅法不能单单从性能上去理解,创建c++,java,c#这样⾯向对象语⾔的⼤师引⼊实例化⽅法⼀定不是要解决什么性能、内存的问题,⽽是为了让开发更加模式化、⾯向对象化。这样说的话,静态⽅法和实例化⽅式的区分是为了解决模式的问题。
接下来继续思考,如果我们全部⽤静态⽅法,不⽤⾮静态⽅法,不是⼀样能实现功能吗?是的,没错,但是你的代码是基于对象,⽽不是⾯向对象的,因为⾯向对象的继承和多态,都是⾮静态⽅法。
第⼆个原因是为什么不建议都⽤静态⽅法,我们如果多线程的情况下,如果静态⽅法使⽤了⼀个静态字段,这个静态字段可以会被多个线程修改,因此说如果在静态⽅法⾥使⽤了静态变量,这就会有线程安全问题,当然了,就算不是多线程,因为静态字段只有⼀份,同样会有被其他地⽅修改的问题。
从这三点我们得出的结论如下:
⼀、 什么时候⽤静态⽅法,什么时候使⽤⾮静态⽅法?
既然静态⽅法和实例化⽅式的区分是为了解决模式的问题,如果我们考虑不需要继承和多态的时候,就可以使⽤静态⽅法,但就算不考虑继承和多态,就⼀概使⽤静态⽅法也不是好的编程思想。
从另⼀个⾓度考虑,如果⼀个⽅法和他所在类的实例对象⽆关,那么它就应该是静态的,否则就应该是⾮静态。因此像⼯具类,⼀般都是静态的。
⼆、 为什么使⽤单例模式⽽不⽤静态⽅法?
从⾯相对象的⾓度讲:
虽然都能实现⽬的,但是他们⼀个是基于对象,⼀个是⾯向对象的,就像我们不⾯相对象也能解决问题⼀样,⾯相对象的代码提供⼀个更好的编程思想。
如果⼀个⽅法和他所在类的实例对象⽆关,那么它就应该是静态的,反之他就应该是⾮静态的。如果我们确实应该使⽤⾮静态的⽅法,但是在创建类时⼜确实只需要维护⼀份实例时,就需要⽤单例模式了。
⽐如说我们在系统运⾏时候,就需要加载⼀些配置和属性,这些配置和属性是⼀定存在了,⼜是公共的,同时需要在整个⽣命周期中都存在,所以只需要⼀份就⾏,这个时候如果需要我再需要的时候new⼀个,再给他分配值,显然是浪费内存并且再赋值没什么意义,所以这个时候我们就需要单例模式或静态⽅法去维持⼀份且仅这⼀份拷贝,但此时这些配置和属性⼜是通过⾯向对象的编码⽅式得到的,我们就应该使⽤单例模式,或者不是⾯向对象的,但他本⾝的属性应该是⾯对对象的,我们使⽤静态⽅法虽然能同样解决问题,但是最好的解决⽅案也应该是使⽤单例模式。
从功能上讲:
单例模式可以控制单例数量;可以进⾏有意义的派⽣;对实例的创建有更⾃由的控制;
三、其他:
数据库连接能不能做SingleTon?
如果是简单地把⼀个connection对象封存在单例对象中,这样是错误的,因此连接池⾥有多个链接可
以⽤,如果使⽤SingleTon,那在WEB访问时,就只能⽤⼀个数据库链接,那不是死的很惨?
但是链接池可以使⽤单例模式,初始化的时候创建譬如100个connection对象,然后再需要的时候提供⼀个,⽤过之后返回到pool 中,我们⽤单例模式,是保证连接池有且只有⼀个。
再举个例⼦,⽐如DAL层写好⼀个调⽤数据库表的类,在BLL层应⽤此类时,如果每次都new创建的话需要频繁的创建和回收,⽽DAL层这个类⾥⼜没有和对象相关的值变量,所以不需要每次都new⼀个,这时候就可以⽤单例模式来创建这个DAL实例。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。