`
iamzhongyong
  • 浏览: 805311 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

关于单例模式(代码篇)

阅读更多

很早的时候,转发过一篇单例模式的文章:http://iamzhongyong.iteye.com/blog/1539642 

最近又翻了一本设计模式的书,然后发现单例其实也简单也复杂,于是就打算把代码敲一下,保存下来。

------------------------------------------------------------------------------------------------------

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package singleton;
/**
 * 最简单的单例模式
 */
public class SimpleSingleton {
 
    /**
     * 构造方法私有化,外部无法通过构造方法创建对象,这样能够屏蔽外部直接new
     * 还有就是反射了,反射时可以使用setAccessible方法来突破private的限制,
     * 我们需要做到第一点工作的同时,还需要在在ReflectPermission("suppressAccessChecks")
     * 权限下使用安全管理器(SecurityManager)的checkPermission方法来限制这种突破,
     * 一般来说,不会真的去做这些事情,都是通过应用服务器进行后台配置实现。
     * 再就是序列化了,序列化会在SimpleSerializableSingleton这个类中做介绍
     */
    private SimpleSingleton(){}
 
    /**
     * 类型是static,这样在JVM进行类加载的时候就会做类的实例化,JVM保证线程安全
     * 根据JLS(Java Language Specification)中的规定,一个类在一个ClassLoader中只会被初始化一次,
     * 这点是JVM本身保证的,那就把初始化实例的事情扔给JVM好了
     */
    private static final SimpleSingleton instance = new SimpleSingleton();
 
    //通过一个静态方法,获得这个对象
    public static SimpleSingleton getInstance(){
        return instance;
    }
}

 

 

 

------------------------------------------------------------------------------------------------------

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
package singleton;
/**
 * 单例模式的懒加载策略,不在类加载的时候进行实例化,而是在第一次调用的时候进行
 */
public class SimpleLazySingleton {
 
    //私有构造方法
    private SimpleLazySingleton(){}
 
    //在类加载的时候,这个对象不进行实例化,volatile变量,拥有可见性
    private static volatile SimpleLazySingleton instance = null;
 
    /**
     * @deprecated
     * 这种会有线程安全问题,因为可能存在多线程访问这个方法,这个时候对象就有可能不是单例的
     */
    public static SimpleLazySingleton getInstanceNotSafe(){
        if(instance == null){
            instance = new SimpleLazySingleton();
        }
        return instance;
    }
 
    /**
     * @deprecated
     * 做一个简单的处理,就是在getInstance的时候添加锁关键字
     * 但是这样有个问题,就是所有的getInstance操作全部加锁,性能会下降很多
     */
    public static synchronized SimpleLazySingleton getInstanceSyncSafe(){
        if(instance == null){
            instance = new SimpleLazySingleton();
        }
        return instance;
    }
 
    /**
     * @deprecated
     * 那就做锁的细化吧,把锁的处理挪到方法体内部,仅仅在instance为空的时候,再去加锁
     */
    public static SimpleLazySingleton getInstanceSyncNotSafe(){
        if(instance == null){
            synchronized (SimpleLazySingleton.class) {
                instance = new SimpleLazySingleton();
            }
        }
        return instance;
    }
 
    /**
     * 那就做锁的细化吧,把锁的处理挪到方法体内部,仅仅在instance为空的时候,再去加锁
     */
    public static SimpleLazySingleton getInstance(){
        if(instance == null){
            synchronized (SimpleLazySingleton.class) {
                /**
                 * 这里称之为double-check-lock,为啥要做这不操作呢?
                 * 因为可能有多个线程进入第一个“if(instance == null)”,这个时候,线程去强占锁,
                 * 抢到锁的线程进行instance的初始化操作,完了之后释放锁,
                 * 第二个线程获得锁,这个时候进入之后,如果没有判空操作,会再一次初始化了实例,这时候就不是单例了
                 */
                if(instance == null){
                    instance = new SimpleLazySingleton();
                }
            }
        }
        return instance;
    }
}

------------------------------------------------------------------------------------------------------

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package singleton;
 
/**
 * 通过Holder的形式来进行,利用JVM的机制来保障线程安全
 */
public class SimpleHolderSingleton {
 
    //私有化
    private SimpleHolderSingleton(){}
 
    //类中有一个私有的XXXHolder类,这个因为是static类型的,所以在JVM加载类的时候就会加载到,但是INSTANCE就不会
    private static class SimpleHolderSingletonHolder{
        //持有外部类的属性
        static final SimpleHolderSingleton INSTANCE = new SimpleHolderSingleton();
    }
 
    //这样会在第一次调用的时候进行初始化操作,因为INSTANCE是static的,所以借助了JVM的机制来保障线程安全
    public static SimpleHolderSingleton getInstance(){
        return SimpleHolderSingletonHolder.INSTANCE;
    }
}

 

 

------------------------------------------------------------------------------------------------------

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package singleton;
 
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
 
/**
 * 如果单例的类实现了序列化接口,这个时候需要做一下特殊处理,
 */
public class SimpleSerializableSingleton implements java.io.Serializable{
 
    private static final long serialVersionUID = -589503673156379879L;
 
    //屏蔽外部new的实例化
    private SimpleSerializableSingleton(){}
 
    private static SimpleSerializableSingleton instance = new SimpleSerializableSingleton();
 
    public static SimpleSerializableSingleton getInstance(){
        return instance;
    }
 
    /**
     * 这个方法,会在发序列化构建对象的时候调用到,如果不这么处理
     * 反序列化之后的对象,是另外一个内存地址,也就是说不再是单例的了
     */
    private Object readResolve() {
        System.out.println("readResolve,被调用了");
        return getInstance(); 
    
 
    public static void main(String[] args) throws Exception {
        SimpleSerializableSingleton simple = SimpleSerializableSingleton.getInstance();
        //获得单例对象的内存地址
        System.out.println(simple);
        //定义序列化写入的文件
        File file = new File("d:\\git\\serializable");
        //构造objectOutputStream
        ObjectOutputStream outStream = new ObjectOutputStream(new FileOutputStream(file));
        //写入对象
        outStream.writeObject(simple);
        outStream.close();
 
        //反序列化
        ObjectInputStream inStream = new ObjectInputStream(new FileInputStream(file));
        SimpleSerializableSingleton simpeFromSeria = (SimpleSerializableSingleton)inStream.readObject();
        System.out.println(simpeFromSeria);
        inStream.close();
    }
 
}

------------------------------------------------------------------------------------------------------

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package singleton;
 
import java.lang.reflect.ReflectPermission;
import java.security.Permission;
/**
 * 如何禁止外部通过反射来做单例对象的序列化
 */
public class SimpleReflectionSingleton {
     
    private SimpleReflectionSingleton(){}
     
    private static SimpleReflectionSingleton instance = new SimpleReflectionSingleton();
     
    public static SimpleReflectionSingleton getInstance(){
        return instance;
    }
    public static void main(String[] args) throws Exception{
         
        //启动JVM的安全检察,在进行反射校验的时候,判断一下是否是“singleton”,如果是,就禁止反射
        System.setSecurityManager(new SecurityManager(){
            @Override
            public void checkPermission(Permission perm) {
                if (perm instanceof ReflectPermission && "suppressAccessChecks".equals(perm.getName())) {
                     for (StackTraceElement elem : Thread.currentThread().getStackTrace()) {
                          if (elem.getClassName().endsWith("Singleton")) {
                              throw new SecurityException();
                          }
                     }
                 }
            }
        });
         
        SimpleReflectionSingleton simple = SimpleReflectionSingleton.getInstance();
        System.out.println(simple);
         
        Class<?> clazz = SimpleReflectionSingleton.class;
         
        SimpleReflectionSingleton ref = (SimpleReflectionSingleton)clazz.newInstance();
         
        System.out.println(ref);
    }
}

3
2
分享到:
评论
4 楼 851228082 2015-04-22  
这是我见过最全面的介绍单例模式的code了。赞!
3 楼 在世界的中心呼喚愛 2014-04-24  
iamzhongyong 写道
last_forever 写道
这个
//类中有一个私有的XXXHolder类,这个因为是static类型的,所以在JVM加载类的时候就会加载到,但是INSTANCE就不会
    private static class SimpleHolderSingletonHolder{
        //持有外部类的属性
        static final SimpleHolderSingleton INSTANCE = new SimpleHolderSingleton();
    }

与这个
private static final SimpleSingleton instance = new SimpleSingleton();


有什么区别呢

第二个是在加载这个类的时候就初始化了。
第一个的话,SimpleHolderSingletonHolder因为是static的,所以在加载的时候会加载进来,但是里面的属性,会在用到的时候才加载,是一种懒加载的模式,通过JVM加载类的机制来保证线程安全的


static 和final会直接读取变量或者对象(我记得随机数不行)
static只是加载class类,需要的时候才会初始化class
2 楼 iamzhongyong 2014-04-23  
last_forever 写道
这个
//类中有一个私有的XXXHolder类,这个因为是static类型的,所以在JVM加载类的时候就会加载到,但是INSTANCE就不会
    private static class SimpleHolderSingletonHolder{
        //持有外部类的属性
        static final SimpleHolderSingleton INSTANCE = new SimpleHolderSingleton();
    }

与这个
private static final SimpleSingleton instance = new SimpleSingleton();


有什么区别呢

第二个是在加载这个类的时候就初始化了。
第一个的话,SimpleHolderSingletonHolder因为是static的,所以在加载的时候会加载进来,但是里面的属性,会在用到的时候才加载,是一种懒加载的模式,通过JVM加载类的机制来保证线程安全的
1 楼 last_forever 2014-04-23  
这个
//类中有一个私有的XXXHolder类,这个因为是static类型的,所以在JVM加载类的时候就会加载到,但是INSTANCE就不会
    private static class SimpleHolderSingletonHolder{
        //持有外部类的属性
        static final SimpleHolderSingleton INSTANCE = new SimpleHolderSingleton();
    }

与这个
private static final SimpleSingleton instance = new SimpleSingleton();


有什么区别呢

相关推荐

    Java 单例模式 工具类

    本篇文章将深入探讨如何在Java中实现单例模式,以及如何创建一个工具类来封装单例的创建。 首先,让我们了解单例模式的几种常见实现方式: 1. 饿汉式(静态常量): 这是最简单的单例实现,它在类加载时就完成了...

    设计模式——策略模式 & 单例模式

    总的来说,策略模式和单例模式都是为了解决特定问题而设计的,它们提供了灵活性和控制力,帮助我们编写更加健壮、可维护的代码。理解并熟练掌握这些设计模式,对于提升软件开发的质量和效率大有裨益。

    工厂模式与单例模式

    在软件设计模式中,工厂模式和单例模式是两种非常基础且重要的模式,它们都是用于解决对象创建问题,但有着不同的设计理念和应用场景。本篇文章将深入探讨这两种模式,并结合具体的代码示例`myFactoryDemo`进行讲解...

    java 设计模式 mvc模式 单例模式 代理 工厂 简单工厂 第二部分

    本篇将深入探讨标题中提及的几种设计模式:Model-View-Controller(MVC)模式、单例模式、代理模式以及工厂模式,尤其是简单工厂模式。 **1. Model-View-Controller (MVC) 模式** MVC模式是一种架构模式,它将应用...

    代理模式、单例模式、工厂模式实例代码

    本篇文章将详细讲解代理模式、单例模式和工厂模式这三种设计模式,并通过Java代码实例进行演示。 1. **代理模式**: 代理模式是一种结构型设计模式,它为一个对象提供一个替身或代理,以控制对该对象的访问。代理...

    Java单例模式设计

    Java单例模式是一种常用的设计模式,它保证一个类只有一个实例,并提供全局访问点。这种模式在需要频繁创建和销毁对象的场景中,或者当对象昂贵时...在实际开发中,选择合适的单例模式可以提升代码的可读性和系统性能。

    JS 设计模式之:单例模式定义与实现方法浅析

    本文实例讲述了JS 设计模式之:单例模式定义与实现方法。分享给大家供大家参考,具体如下: 良好的设计模式可以显著提高代码的可读性,降低复杂度和维护成本。笔者打算通过几篇文章通俗地讲一讲常见的或者实用的设计...

    Delphi-Singleton.rar_delphi_delphi单例模式

    本篇文章将深入探讨 Delphi 中的单例模式以及如何实现。 首先,我们需要理解单例模式的基本概念。单例模式的主要目的是限制类的实例化次数,防止无控制的创建多个实例导致的资源浪费或逻辑错误。在 Delphi 中,这...

    JAVA单例模式(三种)

    Java单例模式是一种常用的软件设计模式,它的核心思想是确保一个类只有一个实例,并提供全局访问点。在Java编程中,单例模式被广泛应用于控制资源的共享,例如数据库连接池、线程池或者日志系统等。在本篇文章中,...

    ios9 单例模式区分arc与非arc宏的使用

    在iOS开发中,单例模式是一种常见的设计模式,它确保一个类只有一个实例,并提供一个全局访问点。在iOS 9中,无论是使用ARC(Automatic Reference Counting)还是非ARC,都可以实现单例,但实现方式有所不同。本篇将...

    Android Singleton单例模式Demo

    在Android开发中,单例模式(Singleton)是一种常用的软件设计模式,它确保一个类只有一个实例,并提供一个全局访问点。这种模式在管理共享资源、控制并发以及减少对象创建的开销等方面非常有用。本篇文章将深入讲解...

    C++中的单例模式.rar

    本篇文章将详细探讨C++中实现单例模式的常见方法,以及它们在实际应用中的考虑因素。 1. **静态成员变量法**: 这是最简单的单例实现方式,通过将实例声明为类的静态成员变量来确保只有一个实例存在。例如: ```...

    js设计模式之单例模式原理与用法详解

    本篇文章将深入探讨单例模式的原理、用法及其在JavaScript中的实现。 首先,我们来理解一下JavaScript中的`apply`和`call`函数。这两个函数都是为了改变函数调用时的上下文(即`this`指向),并允许我们传递参数。`...

    PHP中用Trait封装单例模式的实现

    在本篇文章中,我们将探讨如何使用PHP的Trait特性来封装单例模式,从而简化代码并提高代码的可复用性。 首先,理解单例模式的基本原则。单例模式遵循以下四个步骤: 1. **私有化静态属性**:创建一个私有的静态成员...

    用enum实现单例模式的方法来读取配置文件

    本篇将详细介绍如何利用枚举(enum)来实现单例模式,并结合`Properties`类解析配置文件。 首先,我们来看一下传统的单例模式实现方式,如懒汉式和饿汉式,但这些方法在多线程环境下可能会存在问题。而使用枚举实现...

    单例模式简介以及C++版本的实现

    本篇博文主要内容参考 C++的单例模式一文,在此,为原作者耐心细致的分析讲解,表示感谢。本文将结合此篇文章,给出自己做实验后的理解以及代码,作为学习的小结。  单例模式,它的意图是保证一个类仅拥有一个实例...

    PHP工厂模式、单例模式与注册树模式实例详解

    本篇详细介绍了三种PHP中的设计模式:工厂模式、单例模式与注册树模式,并通过实例来展示它们的基本概念、原理、实现方法及使用技巧。 首先,工厂模式是一种创建型设计模式,它提供了一个创建对象的最佳方式。在...

    单例模式-----<ant求职记之设计模式>

    在《ant求职记之设计模式》这篇博文中,作者可能通过一个生动的故事来解释了单例模式的重要性及其各种实现方式,同时可能探讨了在实际项目中如何选择合适的单例实现策略。通过学习和理解单例模式,开发者可以更好地...

    php单例模式详细介绍及实现

    这篇文章主要介绍了PHP中数据库单例模式的实现代码分享,本文先是讲解了单例模式的一些知识,然后给出了数据库单例模式实现代码。 什么是单例模式 单例模式顾名思义,就是只有一个实例。 作为对象的创建模式, 单例...

Global site tag (gtag.js) - Google Analytics