当前位置: 首页 > 后端技术 > Java

设计模式-单例模式

时间:2023-04-01 19:17:40 Java

为什么1.静态方法常驻内存,非静态方法只在使用时才分配内存?一般认为是这样的,建议使用非静态方法,怕静态方法太占内存。其实,这种认识是错误的。为什么会这样,我们先从内存分配说起:托管堆的定义:对于32位应用程序,在应用程序完成进程初始化后,CLR会在进程的可用地址空间中分配一块保留地址空间,也就是一块内存区域一个进程中的可用地址空间(每个进程可以使用4GB),但不对应任何物理内存。该地址空间是托管堆。托管堆分为多个区域,其中最重要的是垃圾收集堆(GCHeap)和加载堆(LoaderHeap)。GCHeap用于存放对象实例,由GC管理;LoaderHeap分为High-FrequencyHeap、Low-FrequencyHeap和StubHeap,不同的信息存放在不同的堆上。LoaderHeap最重要的信息就是元数据相关的信息,也就是Type对象。每个Type在LoaderHeap上都表示为一个MethodTable(方法表),MethodTable中记录了存储的元数据信息,比如基础Types、静态字段、实现的接口、所有方法等。LoaderHeap不受控制通过GC,它的生命周期是从AppDomain的创建到卸载。(摘自《你必须知道的.Net》)由此我们可以了解到,静态方法和非静态方法其实是放在内存中的MethodTable中的。当一个类第一次被加载时,会把静态方法和非静态方法写入到MethodTable的LoaderHeap中,而LoaderHeap不受GC控制,所以一旦加载,GC不会回收,直到AppDomain已卸载。由此我们也了解到,静态方法和非静态方法,它们都是在第一次加载后常驻内存,所以方法本身是在内存中,没有区别,所以没有结论说“静态方法是驻留在内存中,非静态方法仅在使用时分配”。2.静态方法和非静态方法有什么区别?内存上的区别在于非静态方法创建实例对象时,由于每个对象的属性值不同,当新建一个实例时,会将实例属性复制到GCHeap中,同时time将新对象放入栈中,栈指针指向刚刚复制的实例的内存地址。静态方法不需要,因为静态方法中的静态字段都保存在MethodTable中,只有一份。所以,静态方法和非静态方法,在调用速度上,静态方法肯定更快,因为非静态方法需要实例化和分配内存,而静态方法不需要,但速度上的差异可以忽略不计。3、为什么会有非静态方法?在早期的结构化编程中,几乎所有的方法都是“静态方法”。实例化方法的概念是在面向对象的概念出现之后引入的。静态方法和实例化方法的区别不能仅仅从性能的角度来理解。创建c++、java、c#等面向对象语言的高手引入实例化方法不是为了解决性能和内存问题,而是为了让开发更加模块化和面向对象。这样区分静态方法和实例化方法就是为了解决模式的问题。接下来继续想,如果我们都使用静态方法而不是非静态方法,是不是就可以实现同样的功能了呢?是的,没错,但是你的代码是基于对象的,而不是面向对象的,因为面向对象的继承和多态是非静态方法。第二个原因是为什么不推荐使用静态方法。如果我们有多个线程,如果一个静态方法使用了一个静态字段,这个静态字段可能会被多个线程修改,所以如果在一个静态方法中使用静态变量,这就会有线程安全问题,当然,即使它不是多线程的,因为静态字段只有一份,也会有被其他地方修改的问题。从这三点,我们得出以下结论:1、什么时候使用静态方法,什么时候使用非静态方法?既然区分静态方法和实例化方法是为了解决模式的问题,如果考虑到不需要继承和多态,我们可以使用静态方法,但是即使不考虑继承和多态,使用也是不对的静态方法好的编程思想。从另一个角度来说,如果一个方法与其类的实例对象无关,那么它就应该是静态的,否则就应该是非静态的。因此,它们和工具类一样,一般都是静态的。2、为什么要用单例模式而不是静态方法?从面向对象的角度看:虽然都可以达到目的,但是一个是面向对象的,一个是面向对象的。就像我们不用面向对象也能解决问题一样,面向对象代码提供了更好的编程思想。如果一个方法与其类的实例对象无关,那么它应该是静态的,否则就应该是非静态的。如果我们真的应该使用非静态方法,但我们真的只需要在创建类时维护一个实例,我们就需要使用单例模式。例如,当系统运行时,我们需要加载一些配置和属性。这些配置和属性必须存在并且是公共的。同时,它们需要在整个生命周期中都存在,所以只需要一份。当我需要一个新的时候,给它赋值显然是浪费内存而且赋值是没有意义的,所以这个时候我们就需要单例模式或者静态方法来维护一份且只有一份,但是这个当通过面向对象编码获取这些配置和属性时,我们应该使用单例模式,或者它不是面向对象的,但是它自身的属性应该是面向对象的。虽然我们可以使用静态方法来解决同样的问题,但是最好的解决方法应该还是使用单例模式。从功能上看:单例模式可以控制单例的个数;可以进行有意义的推导;有更多的自由来控制实例的创建;单例模式任何时候只能实例化一次,任何时候,访问这个类的对象都是一样的,只有一个。实现方法1.使用类的publicstatic成员保存唯一对象2.使用publicstatic成员工厂方法3.使用枚举类实现singleton单例破反射来实例化或序列化可能会使单例模式失效。