单例模式

1.定义
保证一个类只有一个实例,并提供一个全局访问点。

2.使用动机
1)系统中只允许有一个(例如操作系统的文件管理器),通过提供一个单例类,由类自身来保证不会创建类的多个实例,类自身来持有它唯一的实例,并且提供一个获取该实例的方法。这就是单例模式。

2)对象提供了一系列的方法供客户端频繁调用,但是又不希望频繁的创建对象,以减少对象创建和GC的时间,可以将对象做成单例模式的。

3)读取系统的配置文件,只需要读取一次放入内存中就可以了,不希望每次读取的时候都加载一次,可以使用单例模式。
3.类图

[img]http://dl2.iteye.com/upload/attachment/0105/1876/81f955f0-3b8d-3054-93e2-fa81e36f1f29.jpg[/img]

这个类图够简单的了吧,就单蹦的一个类,但是单例模式看似简单,其实也不简单,下面慢慢的看它的不简单之处。
其中有3点需要注意的:
1)instance是static的。
2)Singleton()构造方法是private的。//构造方法私有
3)getInstance()方法是public的。//提供全局访问点


4.代码示例
4.1饿汉式
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton(){}
public Singleton getInstance(){
return instance;
}
}


缺点:初始化类的时候就会创建一个实例,可能会造成浪费。
4.2 懒汉式
public class Singleton {
private static Singleton instance ;
private Singleton(){}
public synchronized Singleton getInstance(){
if(instance == null)
instance = new Singleton();
return instance;
}
}

缺点:synchronized会造成线程阻塞,多线程下影响性能。
如果去掉synchronized那么就不是单例模式了,因为多线程模式下可能会同时走到if(instance==null)处,然后都分别创建实例。
4.3 静态内部类
public class StaticSingleton {
private StaticSingleton(){}
private static class StaticSingletonHolder{
private static final StaticSingleton INSTATNCE = new StaticSingleton();
}
public static StaticSingleton getInstance(){
return StaticSingletonHolder.INSTATNCE;
}
}


这种方式比较好,并且也是懒加载的,也是线程安全的,因为虚拟机会保证类的初始化是线程安全的。
4.4 双重校验锁
public class Singleton {
private static volatile Singleton instance ;
private Singleton(){}
public Singleton getInstance(){
if(instance == null){
synchronized (Singleton.class){
if(instance == null)
instance = new Singleton();
}
}
return instance;
}
}
这种写法也比较常见,有2个需要注意的地方:
1)volatile变量。
2)synchronized中还有一个if 是否为空的判断。为什么呢?还是留给读者思考吧。

4.5 enum类型
public enum EnumSingleton {
INSTANCE;
//这里可以写其他的方法
}


这种写法比较少见。
个人比较倾向第3种和第4种写法。
5.其他
5.1序列化
public class SerSingleton implements Serializable{
String name ;
private SerSingleton(){
System.out.println("SerSingleton is created");
name = "SerSingleton";
}
private static SerSingleton instance = new SerSingleton();
public static SerSingleton getInstance(){
return instance;
}
public static void createString(){
System.out.println("createString in SerSingleton");
}
private Object readResolve(){
return instance;
}
}

public class SerSingletonTest {
public static void main(String[] args) throws Exception{
SerSingleton s = null;
SerSingleton s1 = SerSingleton.getInstance();
//写入
FileOutputStream fos = new FileOutputStream("SerSingleton.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(s1);
oos.flush();
oos.close();
//读取
FileInputStream fis = new FileInputStream("SerSingleton.txt");
ObjectInputStream ois = new ObjectInputStream(fis);
s = (SerSingleton)ois.readObject();
System.out.println(s1.equals(s));

}
}
如果注释掉红色字体的readResolve()部分,则输出的结果是false,说明反序列化后又创建了一个实例。

5.2反射
public class SingletonMain {
public static void main(String[] args) throws Exception{
Class clazz = Singleton.class;
Constructor[] constructors = clazz.getDeclaredConstructors();
for(int i=0;i<constructors.length;i++){
Constructor constructor = constructors[i];
constructor.setAccessible(true);
Singleton instance1 = (Singleton) constructor.newInstance();
Singleton instance2 = (Singleton) constructor.newInstance();
System.out.print(instance1 == instance2);
}
}
}


先将构造方法修改为了可见,然后强行的创建了2个实例,最终程序输出了false。
这个有点变态了吧?

5.3不同的类加载器
由于不同的类加载器即使加载同一个类也属于不同的类,所以不同的类加载器可以实例化多个单例模式的对象。


5.4为什么不把属性和方法都定义成静态的实现单例?
使用单例模式有更强的扩展性,假如我改需求了,我要的不是单例,而是可以创建两个对象,那么单例模式很好修改,而都定义成静态方法的类就不好修改了。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值