Java单例模式几种实现方式
在平时的工作、学员的学习以及面试过程中,单例模式作为一种常用的设计模式,会经常被面试官问到,甚至笔试会要求学员现场默写,下面将会就单例模式的实现思路和几种常见的实现方式进行简单的分享。
单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中,应用该模式的类一个类只有一个实例。即一个类只有一个对象实例。是最常用到的设计模式之一,熟悉设计模式的朋友对单例模式都不会陌生。一般介绍单例模式的书籍都会提到 饿汉式 和 懒汉式 这两种实现方式。但是除了这两种方式,本文还会介绍其他几种实现单例的方式。
基本的实现思路
单例模式要求类能够有返回对象一个引用(永远是同一个)和一个获得该实例的方法(必须是静态方法,通常使用getInstance这个名称)。
通俗的讲:即设计一个类,在整个应用中只存在一个对象(需要做到让本类的外部不能够随意创建对象)
单例的实现主要是通过以下三个步骤:
① 构造方法私有化(暂时不考虑反射)
② 在内部创建好一个对象并保存起来
③ 向外提供一个公共的静态的方法,返回内部对象的地址
第一种实现方式:饿汉式[静态常量]的方式
示例代码:
package cn.itsource.sington;
/**
* 饿汉式[静态常量]
*
* @author Administrator
*
*/
public class SingletonTest1 {
/**
* 单例模式 设计一个类 ,让这个 类中只存在一个对象的实例
*
* 1 需要一个私有的 构造方法,这样可以避免外部创建对象
*/
private SingletonTest1() {
}
/*
* 2 在本类内部创建一个 对象 ,保存起来
*/
private static SingletonTest1 instance = new SingletonTest1();
/*
* 3 向外公布一个 公共的 静态的方法 ,返回内部保存的这个对象
*
*/
public static SingletonTest1 getInstance() {
return instance;
}
}
优点:这种写法比较简单,就是在类装载的时候就完成实例化。避免了线程同步问题。
缺点:对象的创建是类加载的时候,可能会导致类加载很慢,没有达到Lazy Loading的效果。如果从始至终从未使用过这个实例,则会造成内存的浪费。
第二种实现方式:饿汉式[静态代码块]的方式
示例代码:
package cn.itsource.sington;
public class SingletonTest3 {
private SingletonTest3() {
}
private static SingletonTest3 instance;
static {
instance = new SingletonTest3();
}
public static SingletonTest3 getInstance() {
return instance;
}
}
这种方式和上面的方式其实类似,只不过将类实例化的过程放在了静态代码块中,也是在类装载的时候,就执行静态代码块中的代码,初始化类的实例。优缺点和上面是一样的。
第三种实现方式:懒汉式[双重检查]的方式
示例代码:
package cn.itsource.sington;
/**
* 懒汉式
* @author Administrator
*/
public class SingletonTest2 {
/**
* 需求:单例模式 设计一个类 ,让这个 类中只存在一个对象的实例
*
* 1 需要一个私有的 构造方法,这样可以避免外部创建对象
*/
private SingletonTest2() {
}
/*
* 2 类加载进内存的时候,对象还没有存在,只有调用了getInstance()方法时,对
象才开始创建
*/
private static SingletonTest2 instance;
/*
* 3 向外公布一个 公共的 静态的方法 ,返回内部保存的这个对象
* 懒汉式是延迟加载,如果多个线程同时操作懒汉式时就有可能出现线程安全问题,
解决线程安全问题:
可以加同步来解决。但是加了同步之后,每一次都要比较锁,效率就变慢了,
所以可以加双重判断来提高程序效率。
*/
public static SingletonTest2 getInstance() {
if (instance == null) {
synchronized (SingletonTest2.class) {
if (instance == null) {
instance = new SingletonTest2();
}
}
}
return instance;
}
}
双重判断,对于多线程开发者来说不会陌生,如代码中所示,我们进行了两次if (instance == null)检查,这样就可以保证线程安全了。这样,实例化代码只用执行一次,后面再次访问时,判断if (instance == null),直接return实例化对象。
优点:线程安全;延迟加载;效率较高。
饿汉式和懒汉式的区别:
1饿汉式是类一加载进内存就创建好了对象;
2懒汉式则是类加载进内存的时候,对象还没有存在,只有调用了getInstance()方法时,对象才开始创建。
3懒汉式是延迟加载,如果多个线程同时操作懒汉式时就有可能出现线程安全问题,解决线程安全问题可以加同步来解决。但是加了同步之后,每一次都要比较锁,效率就变慢了,所以可以加双重判断来提高程序效率。
第四种实现方式:枚举的方式
示例代码:
package cn.itsource.sington;
public enum SingletonTest4 {
INSTANCE;
}
借助JDK1.5中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。可能是因为枚举在JDK1.5中才添加,所以在实际项目开发中,很少见人这么写过。
优点:写法简单这是它最大的优点,其次可以自己处理序列化,是线程安全的
缺点:当想实例化一个单例类的时候,必须要记住使用SingletonTest4.INSTANCE获取对象的方法,而不是使用new,可能会给其他开发人员造成困扰,特别是看不到源码的时候。
以上是对单例模式常见的几种实现方式,在教学和学习过程中的一点简单的总结,希望对大家学习单例模式有一点点帮助。
Java单例模式几种实现方式的更多相关文章
- Java 8 的新特性和Java 的4种引用方式
一.接口的增强 Java 8允许我们给接口添加一个非抽象的方法实现,只需要使用 default关键字即可,这个特征又叫做扩展方法,示例如下: interface Formula { double ca ...
- java的两种同步方式, Synchronized与ReentrantLock的区别
java在编写多线程程序时,为了保证线程安全,需要对数据同步,经常用到两种同步方式就是Synchronized和重入锁ReentrantLock. 相似点: 这两种同步方式有很多相似之处,它们都是加锁 ...
- java集合四种遍历方式
package conection; import java.util.Iterator;import java.util.LinkedList;import java.util.List; publ ...
- Java多线程--两种实现方式
进程概述: 在这之前,有必要了解一下什么是进程? 在一个操作系统中,每个独立的执行的程序都可称为一个进程,也就是"正在运行的程序".如图所示: 线程概述: 如上所述,每个运行的程序 ...
- 转载:Java的四种引用方式
原文:https://www.cnblogs.com/huajiezh/p/5835618.html Java内存管理分为内存分配和内存回收,都不需要程序员负责,垃圾回收的机制主要是看对象是否有引用指 ...
- Java HashMap两种遍历方式
第一种: Map map = new HashMap(); Iterator iter = map.entrySet().iterator(); while (iter.hasNext()) { Ma ...
- JAVA多线程三种实现方式
JAVA多线程实现方式主要有三种:继承Thread类.实现Runnable接口.使用ExecutorService.Callable.Future实现有返回结果的多线程.其中前两种方式线程执行完后都没 ...
- Java 多线程 三种实现方式
Java多线程实现方式主要有三种:继承Thread类.实现Runnable接口.使用ExecutorService.Callable.Future实现有返回结果的多线程.其中前两种方式线程执行完后都没 ...
- 在java 中一种简单方式的声明静态Map常量的方法
我现在需要在一个类里面放一个HashMap,往里面放一些数据,每次要从数据库中取数据的时候先查找HashMap,看是否已经存在,若存在就直接提取,若不存在就从数据库中抽取数据之后再放到HashMap中 ...
随机推荐
- html5-audio 播放列表和自动播放
一个简单audio的列表和播放小例子 <!DOCTYPE html> <html> <head> <meta charset="utf-8" ...
- js 基于可视区域 创建展示区域对应的经纬度二维数组
本篇文章主要是分享下基于地图区域创建经纬度二维数组,需要的朋友可以过来参考下 接上个文章, 基于 地图区域,算出这个展示区域对应的点. 经纬度的变化关系: XY页面展示上, 从左到右维度是增加 如: ...
- Linux 新手入门常用命令
1,增加用户:useradd mylinux passwd mylinux 添加你的用户密码 2,切换用户: su otheruser (注意这种切换方式只是临时的,本质工作目录还在原来的用户目录 ...
- Android实现图片下载并保存SD卡
一.首先获取图片 //第一种获取图片的方法 String filePath = downloadUrl; //以下是取得图片的方法 取得的是InputStream,直接从InputStream生成bi ...
- linux 加jre环境变量
1.vi /etc/profile 2.未行添加 export JAVA_HOME=/usr/local/java export CLASSPATH=.:$JAVA_HOME/lib/tools.ja ...
- 测试拆分比较大SQL Server数据库
有2个办法拆分比较大的数据库. 1.重建聚集索引2.收缩数据库 一.准备测试数据 )) go insert into blocktable(blockno,binno,rack,chipcount,m ...
- Linux入门-9 软件管理基础(CentOS)
0. 源代码形式 1. RPM软件包管理 RPM RPM查询 RPM验证 2. YUM软件管理 YUM基本命令 YUM查询 创建YUM仓库 0. 源代码形式 绝大多数开源软件都是直接以源代码形式发布 ...
- 【Oozie】ambari安装oozie失败
之前对azkaban的研究比较多,现在开个新坑,对Oozie开始深入了解 Traceback (most recent call last): File "/var/lib/ambari-a ...
- php非空验证
我想说这种方法是不是很常用的非空验证,现在的普遍使用的是javascript来验证非空,但是作为学习php的一些知识点,还是可以看看的. 先来看看commit.php中的方法 <?php $db ...
- 一次失败的尝试hdfs的java客户端编写(在linux下使用eclipse)
一次失败的尝试hdfs的java客户端编写(在linux下使用eclipse) 给centOS安装图形界面 GNOME桌面环境 https://blog.csdn.net/wh211212/artic ...