单例模式,属于创建类型的一种常用的软件设计模式。通过单例模式的方法创建的类在当前进程中只有一个实例(根据需要,也有可能一个线程中属于单例,如:仅线程上下文内使用同一个实例。

Java中单例模式定义:“一个类有且仅有一个实例,并且自行实例化向整个系统提供。”

那么如何实现一个单例呢?单例的实现方式有饿汉,懒汉式。懒汉也好,饿汉也好,实现单例的要点就在于让构造方法私有,然后提供公有的静态的方法,对外返回单例的对象实例。懒汉和饿汉的区别在于创建单例对象的时机,一个在类加载时就创建了,一个在调用newInstence方法时才创建。所以,饿汉时实现更为简单,不需要考虑线程安全,但是懒汉式要考虑线程的安全问题。

饿汉式:

public class CatHModel {private String name;private static final CatHModel cat = new CatHModel("Tom");private CatHModel(String name) {this.name = name;}public String getName() {return this.name;}public static CatHModel newInstence(String name) {return cat;}
}

final确保了私有变量cat永远指向一开始创建的名为“Tom"的对象。构造方法私有,无法通过new的方式创建对象。

CatHModel cat = CatHModel.newInstence();
CatHModel cat1 = CatHModel.newInstence();
System.out.println(cat == cat1);

输出为

true

Process finished with exit code 0

可见得到的两个对象地址一致,也就是实际指向的是同一个对象。

懒汉式(线程安全)

public class CatFModel {private String name;private static CatFModel cat;private CatFModel() {}public String getName() {return this.name;}public void setName(String name) {this.name = name;}public static CatFModel newInstence() {if (cat == null) {synchronized (CatFModel.class) {if (cat == null) {cat = new CatFModel();}}}return cat;}
}

我们来测试一下

List<CatFModel> list = Collections.synchronizedList(new ArrayList<>());
int size = 5;
CountDownLatch countDownLatch = new CountDownLatch(size);
for (int i = 0; i < size; i++) {final int temp = i;new Thread(() -> {String name = "Sam" + temp;CatFModel catFModel = CatFModel.newInstence();catFModel.setName(name);list.add(catFModel);countDownLatch.countDown();}).start();
}
try {countDownLatch.await();
} catch (InterruptedException e) {e.printStackTrace();
}
list.forEach(e -> System.out.println(e.getName()));

输出为:

Sam1
Sam1
Sam1
Sam1
Sam1

Process finished with exit code 0

打印的是最后一个线程set的name,因为多个线程使用的是同一个实例,所以最后得到的名字是一样的。多线程下能够保证安全。

但是以上方式实现的单例有一个问题,就是如果通过反射的方式是能够调用私有构造方法创建新对象的。

CatHModel cat = CatHModel.newInstence();
System.out.println(cat.getName());
Constructor constructor = CatHModel.class.getDeclaredConstructor(String.class);
constructor.setAccessible(true);
CatHModel cat2 = (CatHModel) constructor.newInstance("Henry");
System.out.println(cat2.getName());

输出为:

Tom
Henry

Process finished with exit code 0

通过枚举实现单例模式:

public enum CatEnum {INSTANCE;private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}public static CatEnum getInstance(){return INSTANCE;}
}

再来试一下

List<CatEnum> list = Collections.synchronizedList(new ArrayList<>());
int size = 5;
CountDownLatch countDownLatch = new CountDownLatch(size);
for (int i = 0; i < size; i++) {final int temp = i;new Thread(() -> {String name = "Sam" + temp;CatEnum catEnum =  CatEnum.getInstance();catEnum.setName(name);list.add(catEnum);countDownLatch.countDown();}).start();
}
try {countDownLatch.await();
} catch (InterruptedException e) {e.printStackTrace();
}
list.forEach(e -> System.out.println(e.getName()));

输出为

Sam4
Sam4
Sam4
Sam4
Sam4

Process finished with exit code 0

那我们再试一下

Constructor constructor = CatEnum.class.getDeclaredConstructor(String.class,int.class);
constructor.setAccessible(true);
CatEnum cat2 = (CatEnum) constructor.newInstance();
System.out.println(cat2.getName());

输出

Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects
    at java.base/java.lang.reflect.Constructor.acquireConstructorAccessor(Constructor.java:544)
    at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:496)
    at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:483)
    at java_problems.day3.test.main(test.java:67)

会报错,我们看一下具体代码

代码到这里就点不进去了,但是我们看到上面的注释,保证反射的类不是一个枚举类,所以JAVA底层帮助我们做了限制,使得我们无法通过反射的方式破坏单例。

【单例】JAVA中的单例相关推荐

  1. 笔记:Java中的单例设计模式

    之前接触过单例模式,当初不明白这样的设计用意,今天特地研究了下java中的单例设计模式的用处及用法. 单例模式:单例模式是一种常用的软件设计模式.在它的核心结构中只包含一个被称为单例类的特殊类.一个类 ...

  2. Wordcounter,使用Lambdas和Fork / Join计算Java中的单词数

    这些天来,我发布了Wordcounter ,这是一个Java库和命令行实用程序,用于对文本文件中的单词进行计数并对单词计数进行分析,从而大量使用了功能编程结构和并行计算方法. 这是我在"令人 ...

  3. Java基础题37:(单选题)java中char类型的取值范围是() A.0 ... 32767 B.0 ... 65535

    37.(单选题)java中char类型的取值范围是() A.0 - 32767 B.0 - 65535 C.–256 - 255 D.–32768 - 32767 [正确答案]B [答案解析]在jav ...

  4. java中的单例_细说Java中的几种单例模式

    在Java中,单例模式分为很多种,本人所了解的单例模式有以下几种,如有不全还请大家留言指点: 饿汉式 懒汉式/Double check(双重检索) 静态内部类 枚举单例 一.饿汉式 image 饿汉式 ...

  5. java中''和单双引号有什么区别

    区别1:java中的单引号表示字符,java中的双引号是字符串. 区别2:单引号引的数据一般是char类型的:双引号引的数据 是String类型的. 区别3:java中单引号里面只能放一个字母或数字或 ...

  6. 浅谈:数据结构之单链表,java代码演示单链表

    单链表 本文是观看尚硅谷韩老师视频学习总结,部分来源网络. 单链表介绍 链表是一种物理存储单元上非连续.非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的.链表由一系列结点(链表中每 ...

  7. Java中的单例设计模式

    什么是单例设计模式 所谓单例设计模式,就是采取一定的方法保证整个软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法. 目的:使用着在main方法中就不可以自己创建实例对象 ...

  8. java中的单例模型

    参考网址:http://www.runoob.com/design-pattern/singleton-pattern.html 1.目的:保证一个类仅有一个实例,并提供一个访问它的全局访问点.(比如 ...

  9. java中白盒测试用例_基于JAVA开发的中国象棋游戏的开发与研究白盒测试用例.doc...

    中国象棋白盒测试用例 文件状态当前版本V1.0草稿 作 者梁世聪完成日期2012/6/17文档模板SSP-VER-T13-V1.0密 级变更历史 版本完成日期变更记录作者批准签字V1.02012/6/ ...

最新文章

  1. (C++)1023 组个最小数 简单贪心
  2. Idea-LifecycleException when deploying
  3. Ruby Fiber指南(三)过滤器
  4. 前端笔记-StackedBar3D的初步修改(添加legend等)
  5. Spring Cloud Gateway (六) 自定义 Global Filter
  6. 微信工程师为你讲述春晚红包的系统设计和优化
  7. tushare pro积分规则
  8. oracle建表插数据
  9. 雷达散射截面(RCS)
  10. 台式计算机如何取消屏幕密码,台式电脑怎么取消锁屏?
  11. 系统架构设计师-软件架构设计
  12. 离了加多宝 第三季好声音将“变味”
  13. python 对象的销毁_销毁类python的对象
  14. cesium添加填充_项目中的一个关于cesium的边界线的问题
  15. 可爱女生开糖果花店,她两年时间就挣了一百万元
  16. 语音信号的梅尔频率倒谱系数(MFCC)的原理讲解及python实现
  17. 启用群晖 Drive 的团队文件夹
  18. mybatis SQL打印插件
  19. Python下的TK(一)概述
  20. Linux Power supply子系统分析

热门文章

  1. Red_Hat_Linux忘记root密码解决办法
  2. 微信直播相对一般直播平台怎么样?有哪些优势?
  3. STL-priority_queue用法(重点: 升序,小根堆)
  4. 前端vue使用XXTEA进行对称加解密。同时对比rsa算法和xxtea算法的优缺点。
  5. 微前端:使用qiankun框架的注意点
  6. 我所经历的汶川大地震之四
  7. 在接下来的几周,Mt.gox可能会上演一出好戏
  8. 7-4 宿舍谁最高? (20 分)
  9. spring实战学习(四)AOP及其实现方式
  10. 高仿蓝奏云盘下载页面源码