前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >设计模式1:创建型-单例模式

设计模式1:创建型-单例模式

作者头像
张风捷特烈
发布2019-03-04 10:30:22
3470
发布2019-03-04 10:30:22
举报

如果你没有一颗最求完善的心,得过且过,请远离设计模式。 如果你不知道设计原则,请远离设计模式。 如果你为了学习设计模式而学习设计模式,又不是太需要设计模式,请远离设计模式。


单例模式:保证一个类仅有一个实例,并提供一个访问他的全局访问点

单例意味着什么?----崇高与孤独
代码语言:javascript
复制
?--什么样的对象适合用单例?
|--一个对象足以完成任务时,没有必要去创建多个对象
|--一个对象非常消耗资源,需要限制对象的创建

比如:世界,你不会让别人随便去new,如何不让外部无法创建本类对象, 如何给外部提供唯一的对象,是单例模式的核心,要达到这两点很简单。


一、单例的四种形式--形式上的一切都仅是开始而已
1.终极孤独--饿汉

即便无人问津,我也永远存在

饿汉式.png

代码语言:javascript
复制
public class World {
    private static World sWorld = new World();
    //[1]私有化构造
    private World() {
        initWorld();//初始化世界
        System.out.println("世界已创建");
    }
    private void initWorld() {
    }
    //[2]返回内部静态实例
    public static World getInstance() {
        return sWorld;
    }
}

2.有你相伴--懒汉双检锁

虽然我们不是同类,但感谢有你相伴

懒汉双检锁.png

代码语言:javascript
复制
public class World {
    private static volatile World sWorld;
    //[1]私有化构造
    private World() {
        initWorld();//初始化世界
        System.out.println("世界已创建");
    }
    private void initWorld() {
    }
    //[2]返回内部静态实例
    public static World getInstance() {
        if (sWorld == null) {//判断非空后--执行
            synchronized (World.class) {//加锁,保证多线程下的单例
                if (sWorld == null) {//非空,创建实例
                    sWorld = new World();
                }
            }
        }
        return sWorld;
    }
}

3.有你相伴--静态内部类

和上面的功能基本一致,所以我喜欢这个

静态内部类.png

代码语言:javascript
复制
public class World {
    //[1]私有化构造
    private World() {
        initWorld();//初始化世界
        System.out.println("世界已创建");
    }
    private void initWorld() {
    }
    //[3]返回内部静态实例
    public static World getInstance() {
        return WorldHolder.sWorld;
    }
    //[2]创建内部类创建实例
    private static class WorldHolder {
        private static final World sWorld = new World();
    }
}

4.至简--枚举

枚举.png

代码语言:javascript
复制
public enum World {
    INSTANCE;
    World() {
        initWorld();//初始化世界
        System.out.println("世界已创建");
    }
    private void initWorld() {
    }
}

二、单例下的序列化与反射

单例的价值在于一个程序中只用一个该对象实例 如果有恶意份子通过反射创建了另一个世界会怎么样?

1.单例的测试(以静态内部类版为例)

通过debug看出两次获取的都是同一个世界,这就是单一实例

单例测试.png

代码语言:javascript
复制
public class God {
    public static void main(String[] args) {
        World world1 = World.getInstance();
        World world2 = World.getInstance();
    }
}

2.通过反射创建实例

可见world3的内存地址已经不一样了,说明出现了第二个世界,也就是单例的失效 不过应该没有人吃饱了没事干用反射创建单例对象吧,天堂有路你不走...

反射测试.png

代码语言:javascript
复制
public class God {
    public static void main(String[] args) {
        World world1 = World.getInstance();
        World world2 = World.getInstance();
        //通过反射创建
        Class<World> worldClass = World.class;
        try {
            Constructor<World> constructor = worldClass.getDeclaredConstructor(null);
            constructor.setAccessible(true);
            World world3 = constructor.newInstance();
            System.out.println(world3==world2);//false
            System.out.println(world1==world2);//true
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

3.通过反序列化创建对象

如果你的单例类有序列化的需求(如,单例对象本地存储,单例对象网络传输) 反序列化形成的实例也并非原来的实例

反序列化.png

代码语言:javascript
复制
---->[World]-------------
public class World implements Serializable {

---->[God]-------------
public class God {
    public static void main(String[] args) {
        World world1 = World.getInstance();
        World world2 = World.getInstance();

        //通过反射创建
        Class<World> worldClass = World.class;
        try {
            Constructor<World> constructor = worldClass.getDeclaredConstructor(null);
            constructor.setAccessible(true);
            World world3 = constructor.newInstance();
            System.out.println(world3 == world2);//false
            System.out.println(world1 == world2);//true
        } catch (Exception e) {
            e.printStackTrace();
        }
        //通过反序列化创建对象
        try {
            //序列化输出
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("world.obj"));
            oos.writeObject(world1);
            //反序列化创建对象
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("world.obj"));
            World world4 = (World) ois.readObject();
            ois.close();
            System.out.println(world1 == world4);//false
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

4.发序列化的解决方案

通过反序列化时的钩子函数:readResolve来控制序列化对象实例

反序列化的防治.png

代码语言:javascript
复制
---->[World]-------------
//解决反序列化创建实例的问题,readResolve创建的对象会直接替换io流读取的对象
private Object readResolve() throws ObjectStreamException {
    return getInstance();
}

三、结尾小述
1.优缺点
代码语言:javascript
复制
优点:
设计者严格管控这个类,全局作用。简化使用者使用
只创建一个实例,避免频繁创建销毁对象,节省内存资源,减少系统开销

缺点:
没有抽象层,不利于扩展
职责过重,违背单一职责原则
2.常见的单例
代码语言:javascript
复制
java.util.Calendar 标准单例,通过Calendar.getInstance方法获取对象
java.lang.System 完全单例,不提供外部构造方法,全部以静态方法提供服务
android.view.LayoutInflater 标准单例 ,通过LayoutInflater.from(Context)方法获取对象

后记:捷文规范
1.本文成长记录及勘误表

项目源码

日期

附录

V0.1--github

2018-2-10

发布名:设计模式1:创建型-单例模式 捷文链接:https://cloud.tencent.com/developer/article/1397732

2.更多关于我

笔名

QQ

微信

张风捷特烈

1981462002

zdl1994328

我的github:https://github.com/toly1994328 我的简书:https://www.jianshu.com/u/e4e52c116681 我的掘金:https://juejin.im/user/5b42c0656fb9a04fe727eb37 个人网站:http://www.toly1994.com

3.声明

1----本文由张风捷特烈原创,转载请注明 2----欢迎广大编程爱好者共同交流 3----个人能力有限,如有不正之处欢迎大家批评指证,必定虚心改正 4----看到这里,我在此感谢你的喜欢与支持

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019.02.10 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 单例意味着什么?----崇高与孤独
  • 一、单例的四种形式--形式上的一切都仅是开始而已
    • 1.终极孤独--饿汉
      • 2.有你相伴--懒汉双检锁
        • 3.有你相伴--静态内部类
          • 4.至简--枚举
          • 二、单例下的序列化与反射
            • 1.单例的测试(以静态内部类版为例)
              • 2.通过反射创建实例
                • 3.通过反序列化创建对象
                  • 4.发序列化的解决方案
                  • 三、结尾小述
                    • 1.优缺点
                      • 2.常见的单例
                      • 后记:捷文规范
                        • 1.本文成长记录及勘误表
                          • 2.更多关于我
                            • 3.声明
                            领券
                            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档