过气的,终究是过气了
上一章简单介绍了 UML 类图(二), 如果没有看过,请观看上一章
所谓的单例设计模式,就是采用一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,
并且该类只提供一个取得其对象实例的方法 (静态方法)
引用 菜鸟教程里面的单例模式介绍: https://www.runoob.com/design-pattern/singleton-pattern.html
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,
它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。
这个类提供了一种访问其唯一的对象的方式,
可以直接访问,不需要实例化该类的对象。
注意:
意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
主要解决:一个全局使用的类频繁地创建与销毁。
何时使用:当您想控制实例数目,节省系统资源的时候。
如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建。
关键代码:构造函数是私有的。
应用实例:
优点:
缺点: 没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
使用场景:
注意事项:getInstance() 方法中需要使用同步锁 synchronized (Singleton.class)
防止多线程同时进入造成 instance 被多次实例化。
直接构建对象
public class Single01 {
/**
类的内部创建对象
final static
*/
private final static Single01 instance = new Single01();
/**
构建方法地私有化
*/
private Single01() {
}
/**
对外提供一个静态的公共方法
*/
public static Single01 getInstance() {
return instance;
}
}
测试方法:
@Test
public void oneTest() {
Single01 single01 = Single01.getInstance();
Single01 single02 = Single01.getInstance();
log.info("是否相同: {}", single01 == single02);
log.info(" hashcode 是否相同: {}", single01.hashCode() == single02.hashCode());
}
优点: 写法比较简单,就是在类装载的时候就完成实例化。 避免了线程同步的问题。
缺点: 在类装载的时候就完成实例化,没有达到 Lazy Loading 懒加载的效果。
如果从始至终从未使用过这个变量,则会造成内存的浪费。
这种方式 基于 classloader 机制避免了多线程同步的问题,不过 instance 在类装载时就进行实例化,
在单例模式中大多数都是调用 getInstance() 方法,但是导致类装载的原因有多种,因此不能确定有其他的方式 (其他的静态方法)
导致类装载, 这时候初始化 instance 就没有达到 lazy loading 的效果。
结论: 这种单例模式可用,但可能会造成内存浪费。
静态代码块里面构建对象
public class Single02 {
private static Single02 instance ;
static {
instance = new Single02();
}
private Single02 (){
}
public static Single02 getInstance() {
return instance;
}
}
@Test
public void twoTest() {
Single02 single01 = Single02.getInstance();
Single02 single02 = Single02.getInstance();
log.info("是否相同: {}", single01 == single02);
log.info(" hashcode 是否相同: {}", single01.hashCode() == single02.hashCode());
}
方法中 验证 为空 再进行构建对象
public class LanSingle03 {
private static LanSingle03 instance;
private LanSingle03 (){
}
public static LanSingle03 getInstance() {
if (instance == null) {
instance = new LanSingle03();
}
return instance;
}
}
测试方法:
@Test
public void threeTest() {
LanSingle03 single01 = LanSingle03.getInstance();
LanSingle03 single02 = LanSingle03.getInstance();
log.info("是否相同: {}", single01 == single02);
log.info(" hashcode 是否相同: {}", single01.hashCode() == single02.hashCode());
}
但是该方式在多线程环境下会存在并发问题
@Test
public void threadTest() throws Exception{
for( int i = 0; i< 20; i++) {
new Thread(()->{
log.info(">>> 打印实例: {}", LanSingle03.getInstance());
},i+"").start();
}
TimeUnit.SECONDS.sleep(2);
}
结论: 在实际开发中,不要使用这种方式
方法上添加 synchronized 进行同步
public class LanSingle04 {
private static LanSingle04 instance;
private LanSingle04(){
}
public synchronized static LanSingle04 getInstance() {
if (instance == null) {
instance = new LanSingle04();
}
return instance;
}
}
@Test
public void fourTest() {
LanSingle04 single01 = LanSingle04.getInstance();
LanSingle04 single02 = LanSingle04.getInstance();
log.info("是否相同: {}", single01 == single02);
log.info(" hashcode 是否相同: {}", single01.hashCode() == single02.hashCode());
}
方法中,为空时, 同步类,同步代码块内部进行实例化
public class LanSingle05 {
private static LanSingle05 instance;
private LanSingle05(){
}
public static LanSingle05 getInstance() {
if (instance == null) {
synchronized (LanSingle05.class){
instance = new LanSingle05();
}
}
return instance;
}
}
@Test
public void fiveTest() {
LanSingle05 single01 = LanSingle05.getInstance();
LanSingle05 single02 = LanSingle05.getInstance();
log.info("是否相同: {}", single01 == single02);
log.info(" hashcode 是否相同: {}", single01.hashCode() == single02.hashCode());
}
有线程安全的问题:
@Test
public void threadTest() throws Exception{
for( int i = 0; i< 20; i++) {
new Thread(()->{
log.info(">>> 打印实例: {}", LanSingle05.getInstance());
},i+"").start();
}
TimeUnit.SECONDS.sleep(2);
}
结论: 在实际开发中,不推荐使用这种方式
双重检查,在 同步代码块内部,再判断一下是否为空, 为空才进行实例化
public class CheckSingle06 {
private static CheckSingle06 instance;
private CheckSingle06(){
}
public static CheckSingle06 getInstance() {
if (instance == null) {
synchronized (CheckSingle06.class){
if (instance == null) {
instance = new CheckSingle06();
}
}
}
return instance;
}
}
@Test
public void sexTest() {
CheckSingle06 single01 = CheckSingle06.getInstance();
CheckSingle06 single02 = CheckSingle06.getInstance();
log.info("是否相同: {}", single01 == single02);
log.info(" hashcode 是否相同: {}", single01.hashCode() == single02.hashCode());
}
定义一个静态的内部类, 内部类中属性进行构建
public class InnerSingle07 {
private InnerSingle07(){
}
private static class InnerClass {
private static final InnerSingle07 INSTANCE = new InnerSingle07();
}
public static InnerSingle07 getInstance() {
return InnerClass.INSTANCE;
}
}
测试方法:
@Test
public void sevenTest() {
InnerSingle07 single01 = InnerSingle07.getInstance();
InnerSingle07 single02 = InnerSingle07.getInstance();
log.info("是否相同: {}", single01 == single02);
log.info(" hashcode 是否相同: {}", single01.hashCode() == single02.hashCode());
}
枚举 enum
public enum EnumSingle08 {
INSTANCE("1");
private String name;
EnumSingle08(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
@Test
public void nightTest() {
EnumSingle08 single01 = EnumSingle08.INSTANCE;
EnumSingle08 single02 = EnumSingle08.INSTANCE;
log.info("是否相同: {}", single01 == single02);
log.info(" hashcode 是否相同: {}", single01.hashCode() == single02.hashCode());
}
java.lang.Runtime 类
是饿汉式第一种
本章节的代码放置在 github 上:
https://github.com/yuejianli/DesignPattern/tree/develop/Single
谢谢您的观看,如果喜欢,请关注我,再次感谢 !!!