当前位置:首页 > 服务端 > java常用设计模式(一)单例模式

java常用设计模式(一)单例模式

2022年09月16日 19:18:00服务端2

  第一次写博客,也是第一篇,从单例模式开始,不足之处,望各位看官海涵。

 简介

  首先我们都知道单例模式是java常用的23种设计模式之一,它的用途可谓是非常广泛。它的核心就在于单实例,即整个环境中该类有且只能有一个对象。而java创建实例的方式已知的有四种,分别是通过new、clone、反射或者序列化这四种方式去创建实例,怎样保证单例呢,下面且听我一一道来。

 单例模式的常见写法:

  1.基础饿汉式单例

    优点:

    类加载时就去初始化,没有线程安全问题,不能通过new创建实例

    缺点:

    ①.能通过反射或者序列化去创建新的实例(解决方式在最后)

    ②.类加载时就创建好对象,可能会创建出来用不到的实例对象,这样对内存是种浪费

/**
 * 基础饿汉式单例
 */
public class HungrySingleton {
    
    private static final HungrySingleton hungrySingleton = new HungrySingleton();

    private HungrySingleton(){}

    public static HungrySingleton getInstance(){
        return hungrySingleton;
    }
}

  2.简单懒汉式

    优点:

    懒汉式和饿汉式不同,它不需要在类加载的时候去创建对象,而是在类实例化的时候才会去创建对象,所以不存在内存空间浪费

    缺点:

    ①.懒汉式是线程不安全的,多个线程同时去实例化该对象有可能会生成多个实例对象,破坏单例

    ②.能通过反射或者序列化去创建新的实例(解决方式在最后)

/**
 * 普通的懒汉式
 */
public class LazySingleton {

    private static LazySingleton lazySingleton;

    private LazySingleton(){}

    public static LazySingleton getInstance(){
        if(lazySingleton == null){
            lazySingleton = new LazySingleton();
        }
        return lazySingleton;
    }
}

  3.懒汉式二(双重校验锁-double check)

    优点:

    解决了简单懒汉式的线程安全问题

    缺点:

    ①.加入synchronized关键字,一定程度上影响性能

    ②.能通过反射或者序列化去创建新的实例(解决方式在最后)

/**
 * 双重校验锁
 */
public class DoubleCheckSingleton {

    private DoubleCheckSingleton doubleCheckSingleton;

    private DoubleCheckSingleton(){}

    public DoubleCheckSingleton getInstance(){
        if(doubleCheckSingleton == null){
            synchronized (DoubleCheckSingleton.class){
                if(doubleCheckSingleton == null){
                    doubleCheckSingleton =  new DoubleCheckSingleton();
                }
            }
        }
        return doubleCheckSingleton;
    }
}

  4.懒汉式三(静态内部类)

   优点:

   没有synchronized关键字,不会影响性能,也没有线程安全问题

   缺点:

   能通过反射或者序列化去创建新的实例(解决方式在最后)

/**
 * 静态内部类
 */
public class StaticInnerClassSingleton implements Serializable {

    private StaticInnerClassSingleton(){}

    public static StaticInnerClassSingleton getInstance(){
        return InnerClass.staticInnerClassSingleton;
    }

    static class InnerClass{
        private static StaticInnerClassSingleton staticInnerClassSingleton = new StaticInnerClassSingleton();
    }
}

  5.枚举类型单例

    优点:枚举的优点就是没有缺点(我本人没有发现,各位大佬有知道的可以告诉我)

/**
 * 枚举类型单例
 */
public enum EnumSingleton {

    INSTANCE;

    public static EnumSingleton getInstance(){
        return INSTANCE;
    }

}

 反射和序列化破坏单例的解决办法

  反射和序列化是破坏单例的两种常见方式,我们在静态内部类的基础上添加解决方案。

  首先我们为该类实现Serializable接口,重写readResolve方法,这样能够解决序列化破坏单例的问题;其次,我们在原本空的构造方法中添加一段代码,去判断实例是否已经存在,如果存在则抛异常,这样能够解决反射破坏单例的问题 

import java.io.Serializable;

/**
 * 静态内部类
 */
public class StaticInnerClassSingleton implements Serializable {


    private StaticInnerClassSingleton(){
        if(InnerClass.staticInnerClassSingleton != null){
            throw new RuntimeException("不允许创建多个实例");
        }
    }

    public static StaticInnerClassSingleton getInstance(){
        return InnerClass.staticInnerClassSingleton;
    }

    static class InnerClass{
        private static StaticInnerClassSingleton staticInnerClassSingleton = new StaticInnerClassSingleton();
    }

    //保证序列化不回破坏单例
    private Object readResolve(){
        return InnerClass.staticInnerClassSingleton;
    }
}

 总结

  简单懒汉式是我们极不推荐的一种形式,因为会存在线程安全的问题,双重校验锁和基础饿汉式是通过牺牲一定性能或者空间来达到实现单例的目的,如果性能要求不高或者内存空间足够的话,可以酌情使用。我们更加推荐的形式是静态内部类和枚举类型的单例,尤其是枚举类型,不需要通过额外的代码去防止序列化和反射破坏单例,这是jdk开发者在源码层面就做过限制的,相对而言,静态内部类虽然需要自己手动去做校验,但是它简单易懂,很容易让人理解。

 

作者:BlueAndWhite
来源链接:https://www.cnblogs.com/blue-and-white/p/10987144.html

版权声明:
1、JavaClub(https://www.javaclub.cn)以学习交流为目的,由作者投稿、网友推荐和小编整理收藏优秀的IT技术及相关内容,包括但不限于文字、图片、音频、视频、软件、程序等,其均来自互联网,本站不享有版权,版权归原作者所有。

2、本站提供的内容仅用于个人学习、研究或欣赏,以及其他非商业性或非盈利性用途,但同时应遵守著作权法及其他相关法律的规定,不得侵犯相关权利人及本网站的合法权利。
3、本网站内容原作者如不愿意在本网站刊登内容,请及时通知本站(javaclubcn@163.com),我们将第一时间核实后及时予以删除。


本文链接:https://www.javaclub.cn/server/42278.html

分享给朋友:

“java常用设计模式(一)单例模式” 的相关文章

一分钟搞定Java环境变量配置

一分钟搞定Java环境变量配置

对于学Java的人来说,成功配置环境变量是第一步,因为后期不论 你做什么工作,会发现都需要这些,接下来介绍如何安装与配置,我按照jdk1.6来说明,其他一致。 下载官网 首先将jdk安装好后进行配置。 右击“计算机”,右键打开“属性”,...

Java实现Email发送

一、前言最近将项目的登录密码从图形验证码改为了短信验证码,同时也将忘记密码时长度进行了修改,在修改时,想到了之前在一些国外的网站上,使用过邮箱接收验证码的情况,故想到何妨不自己尝试整合一下Java程序发送邮件信息呢,所以动手整合了Email的发送实例。二、Email发送协议想要在互联网上提供电子邮件...

什么是设计模式?详解设计模式概念及几大原则

什么是设计模式?详解设计模式概念及几大原则

​ hello,各位小伙伴大家好,又到了一篇一次的斗图环节,天气冷了,各位注意保暖。 今天我们来讲讲设计模式,在我们学习Java的时候,时常听到单例模式,多例模式,还有使用Spring的时候,默认采用的单例模式,你所听到的“饱汉式”、“饿汉式”,都是对设计模式的形容。那么什么是设计模式呢?...

常用设计模式系列(四)—建造者模式

常用设计模式系列(四)—建造者模式

一、前言 “山不在高,有仙则名。水不在深,有龙则灵。斯是陋室,惟吾德馨。苔痕上阶绿,草色入帘青。谈笑有鸿儒,往来无白丁。可以调素琴,阅金经。无丝竹之乱耳,无案牍之劳形。南阳诸葛庐,西蜀子云亭。孔子云:何陋之有?” ​ ——《陋室铭》刘禹锡 ​ 唐代诗人刘禹锡使用短...

java基础知识讲解(一)数据类型和运算符

java基础知识讲解(一)数据类型和运算符

Java是一种强类型语言,每个变量都必须声明其数据类型。 Java的数据类型可分为两大类:基本数据类型(primitive data type)和引用数据类型(reference data type)。 Java中定义了3类8种基本数据类型 数值型- b...

Java 并发核心机制

Java 并发核心机制

📦 本文以及示例源码已归档在 javacore 一、J.U.C 简介 Java 的 java.util.concurrent 包(简称 J.U.C)中提供了大量并发工具类,是 Java 并发能力的主要体现(注意,不是全部,有部分并发能力的支持在其他包中)。...

Java虚拟机1:什么是Java

Java虚拟机1:什么是Java

前言 让我们来看一下Java的广告词,来自http://www.java.com/zh_CN/about/: 97%的企业桌面运行Java 美国有89%的桌面(或计算机)运行Java 全球有900万Java开发人员 开发人员的头号选择...

Java 日志框架详解

Java 日志框架详解

1. JUL学习 JUL全称Java util Logging是java原生的日志框架,使用时不需要另外引用第三方类库,相对其他日志框 架使用方便,学习简单,能够在小型应用中灵活使用。 1.1 架构介绍 Loggers...

Java实现素数的判断

素数的定义只能被1和它本身整除,不包括1 例 2.3.5.7.11.13 实现代码 Scanner in=new Scanner(System.in); int n ; n=in.nextInt(); for(int n1=2;n1&l...

java数数字及while和do while 的使用,以及程序的调试与验证

while的条件是在进入循环体之前判断的,执行完一轮循环之后,会再回到循环开始的地方再次判断条件,而不会在循环体中随时判断条件 1.while语句是当条件满足时不断的执行循环体内语句。 2.会提前判断是否满足条件,所以有可能一次也没有执行。 3.条件成立...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。