1 为什么有的工具类使用单例不使用static?

首先你要明白static 是在什么时候初始化的,其设计意图是是什么,

单例 就是我们运行的当前虚拟机中有且只有一个需要的对象,不存在重复。 

static 是给类静态成员变量使用的,属于类的属性,一般是一些常量之类的东西,从加载上来说对于类和对象之间,在类加载到内存时候静态成员变量就存在了,而对象还不存在,

    另外 静态方法只能调用静态方法和静态变量这个你也应该知道,如果全部搞成静态方法那么意味着其他成员变量都要是静态的,很不方便,如果一天不要单例了也不容易扩展,很麻烦。

单例更符合OO思想。

另外,如果有static变量,必须考虑多线程的问题。

最近用sonar测评代码质量的时候,发现一个问题,工程中一些util类,以前写的static方法都提示最好用单例的方式进行改正。

为此,我仔细想了想,发现还是很有道理的。这里谈谈我个人对static方法与单例模式的理解。

这里我谈谈两种写法:

以前一些公共的类,我不假思索,习惯按照如下写法去写:

public class DateUtil {
    public final static String DATEFORMAT = "YYYY-MM-DD";
    private DateUtil() {
    }
    public static void changeDateFormat() {
    }
}

 

  调用的时候直接DateUtil.changeDateFormat();

  如果希望这个类作为一个单例,OK,可以直接在方法里添加一个私有的构造方法。

  这样一来,既可以避免用户new一个对象,又可以实现方法的调用,看似很好。

    其实,这种做法最大的缺点就是static作为一个静态方法,在加载类的时候就被加载到内存中,不管你用不用都占用这个位置,这种设计是不推荐的

 

 

2 几种单例模式的介绍

第一种(懒汉,线程不安全)

Java代码

单例模式详解_第1张图片





public class Singleton {

    private static Singleton instance;

    private Singleton (){}

 

    public static Singleton getInstance() {

if (instance == null) {

    instance = new Singleton();

}

return instance;

    }

}

 

 这种写法lazy loading(懒加载)很明显,但是致命的是在多线程不能正常工作。

 

第二种(懒汉,线程安全)

Java代码

单例模式详解_第2张图片public class Singleton {


    private static Singleton instance;

    private Singleton (){}

    public static synchronized Singleton getInstance() {

if (instance == null) {

    instance = new Singleton();

}

return instance;

    }

}

 

 这种写法能够在多线程中很好的工作,而且看起来它也具备很好的lazy loading,但是,遗憾的是,效率很低,99%情况下不需要同步。

 

 

 

第三种(饿汉)

 

Java代码

单例模式详解_第3张图片 

public class Singleton {

    private static Singleton instance = new Singleton();

    private Singleton (){}

    public static Singleton getInstance() {

return instance;

    }

}

 

 这种方式基于classloder机制避免了多线程的同步问题,不过,instance在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用getInstance方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化instance显然没有达到lazy loading的效果。

 

第四种(饿汉,变种)

Java代码

单例模式详解_第4张图片public class Singleton {

    private Singleton instance = null;

    static {

instance = new Singleton();

    }

    private Singleton (){}

    public static Singleton getInstance() {

return this.instance;

    }

}

 

 表面上看起来差别挺大,其实更第三种方式差不多,都是在类初始化即实例化instance。

外面开发用第三,第四种比较多.在很多框架中,也会经常看到第四种的身影. 大家现在开始都使用第四种.

 

种(枚举):

 

Java代码

public enum Singleton {

    INSTANCE;

    public void whateverMethod() {

    }

}

 单例模式详解_第5张图片

 这种方式是Effective Java作者Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象,可谓是很坚强的壁垒啊.

这种方式verygood.

 

3 单例模式的优缺点

优点

● 由于单例模式在内存中只有一个实例,减少了内存开支,特别是一个对象需要频繁地

创建、销毁时,而且创建或销毁时性能又无法优化,单例模式的优势就非常明显。

● 由于单例模式只生成一个实例,所以减少了系统的性能开销,当一个对象的产生需要

比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一

个单例对象,然后用永久驻留内存的方式来解决(在Java EE中采用单例模式时需要注意JV M

垃圾回收机制)。

● 单例模式可以避免对资源的多重占用,例如一个写文件动作,由于只有一个实例存在

内存中,避免对同一个资源文件的同时写操作。

● 单例模式可以在系统设置全局的访问点,优化和共享资源访问,例如可以设计一个单

例类,负责所有数据表的映射处理。

:性能优化

 

缺点

●  单例模式一般没有接口,扩展很困难,若要扩展,除了修改代码基本上没有第二种途

径可以实现。单例模式为什么不能增加接口呢?因为接口对单例模式是没有任何意义的,它

要求“自行实例化”,并且提供单一实例、接口或抽象类是不可能被实例化的。当然,在特殊情况下,单例模式可以实现接口、被继承等,需要在系统开发中根据环境判断。

●  单例模式对测试是不利的。在并行开发环境中,如果单例模式没有完成,是不能进行

测试的

●  单例模式与单一职责原则有冲突。一个类应该只实现一个逻辑,而不关心它是否是单

例的,是不是要单例取决于环境,单例模式把“要单例”和业务逻辑融合在一个类中。

:不得于扩展与测试

 

4 何时使用单例模式

在一个系统中,要求一个类有且仅有一个对象,如果出现多个对象就会出现“不良反

应”,可以采用单例模式,具体的场景如下:

●  要求生成唯一序列号的环境;

●  在整个项目中需要一个共享访问点或共享数据,例如一个Web页面上的计数器,可以

不用把每次刷新都记录到数据库中,使用单例模式保持计数器的值,并确保是线程安全的;

●  创建一个对象需要消耗的资源过多,如要访问I O和数据库等资源;

●  需要定义大量的静态常量和静态方法(如工具类)的环境,可以采用单例模式(当

然,也可以直接声明为st at ic的方式)。