Java单例模式的实现与破坏
单例模式是一种设计模式,是在整个运行过程中只需要产生一个实例。那么怎样去创建呢,以下提供了几种方案。
一、创建单例对象
懒汉式
public class TestSingleton {
// 构造方法私有化
private TestSingleton(){}
// 声明实例
private static TestSingleton singleton;
// 提供外部调用方法,生成并获取实例
public static TestSingleton getInstance() {
if(singleton == null) {
singleton = new TestSingleton();
}
return singleton;
}
}
此方案是以时间换空间,启动时并不会执行任何操作,只有被调用时,采取实例化对象。不过这种方法在多线程下不安全,因为两个线程如果同时调用时,会同时通过非空验证的验证,造成创建两个对象的后果,有悖设计初衷。
针对多线程问题,应该加入双重非空判断:
public class TestSingleton {
// 构造方法私有化
private TestSingleton(){}
// 声明实例
private static volatile TestSingleton singleton;
// 提供外部调用方法,生成并获取实例
public static TestSingleton getInstance() {
if(singleton == null) {
synchronized (TestSingleton.class) {
if(singleton == null) {
singleton = new TestSingleton();
}
}
}
return singleton;
}
}
饿汉式
public class TestSingleton {
// 构造方法私有化
private TestSingleton(){}
// 声明并生成实例
private static TestSingleton singleton = new TestSingleton();
// 提供外部调用方法,获取实例
public static TestSingleton getInstance() {
return singleton;
}
}
以空间换时间,类一加载时,就对其进行实例化,后面调用时直接提供对象实例。
静态内部类实现懒加载
public class TestSingleton {
// 构造方法私有化
private TestSingleton(){}
private static class TestSingletonFactory{
private static TestSingleton singleton = new TestSingleton();
}
// 提供外部调用方法,获取实例
public static TestSingleton getInstance() {
return TestSingletonFactory.singleton;
}
}
当getInstance方法被调用时,才会初始化静态内部类TestSingletonFactory的静态变量singleton。此处由JVM来保障线程安全。
二、破坏单例
实现单例后,按照预期结果应该所有对象都是同一个对象。但是以下有几种情况可以破坏单例的性质。
首先让单例类实现Serializable, Cloneable接口,以便实验。
public class TestSingleton implements Serializable, Cloneable{
private static final long serialVersionUID = 1L;
// 构造方法私有化
private TestSingleton(){}
private static class TestSingletonFactory{
private static TestSingleton singleton = new TestSingleton();
}
// 提供外部调用方法,获取实例
public static TestSingleton getInstance() {
return TestSingletonFactory.singleton;
}
}
- 序列化
// 获取实例
TestSingleton originSingleton = TestSingleton.getInstance();
// 写出对象
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(originSingleton);
// 写入对象
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
TestSingleton serializeSingleton = (TestSingleton) ois.readObject();
// 判断两个对象是否相等
System.out.println(originSingleton == serializeSingleton); // false
- 反射
// 反射
Class<TestSingleton> clazz = TestSingleton.class;
// 获取无参构造函数
Constructor<TestSingleton> constructor = clazz.getDeclaredConstructor();
// 将私有设置为可见
constructor.setAccessible(true);
// 用构造器生成实例
TestSingleton instance = constructor.newInstance();
// 判断两个对象是否相等
System.out.println(originSingleton == instance); // false
- 克隆
// 克隆
TestSingleton clone = (TestSingleton) originSingleton.clone();
System.out.println(originSingleton == clone); // false
三、修复破坏
对于这种预料之外的结果,我们应该怎样去控制呢?
- 序列化
添加readResolve方法,返回Object。
- 反射
添加全局可见变量,如果再次调用构造方法生成实例时,抛出运行时错误。
- 克隆
重写clone方法,直接返回单例对象。
public class TestSingleton implements Serializable, Cloneable{
private static final long serialVersionUID = 1L;
private static volatile boolean isCreated = false;//默认是第一次创建
// 构造方法私有化
private TestSingleton(){
if(isCreated) {
throw new RuntimeException("实例已经被创建");
}
isCreated = true;
}
private static class TestSingletonFactory{
private static TestSingleton singleton = new TestSingleton();
}
// 提供外部调用方法,获取实例
public static TestSingleton getInstance() {
return TestSingletonFactory.singleton;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return getInstance();
}
/**
* 防止序列化破环
* @return
*/
private Object readResolve() {
return getInstance();
}
}
Java单例模式的实现与破坏的更多相关文章
- 用java单例模式实现面板切换
1.首先介绍一下什么是单例模式: java单例模式是一种常见的设计模式,那么我们先看看懒汉模式: public class Singleton_ { //设为私有方法,防止被外部类引用或实例 priv ...
- 【深入】java 单例模式(转)
[深入]java 单例模式 关于单例模式的文章,其实网上早就已经泛滥了.但一个小小的单例,里面却是有着许多的变化.网上的文章大多也是提到了其中的一个或几个点,很少有比较全面且脉络清晰的文章,于是,我便 ...
- 深入Java单例模式(转)
深入Java单例模式 源自 http://devbean.blog.51cto.com/448512/203501 在GoF的23种设计模式中,单例模式是比较简单的一种.然而,有时候越是简单的东西越容 ...
- Java 单例模式的七种写法
Java 单例模式的七种写法 第一种(懒汉,线程不安全) public class Singleton { private static Singleton instance; private Sin ...
- java单例模式之懒汉式分析
转自:http://blog.csdn.net/withiter/article/details/8140338 今天中午闲着没事,就随便写点关于Java单例模式的.其实单例模式实现有很多方法,这里我 ...
- Java 单例模式探讨
以下是我再次研究单例(Java 单例模式缺点)时在网上收集的资料,相信你们看完就对单例完全掌握了 Java单例模式应该是看起来以及用起来简单的一种设计模式,但是就实现方式以及原理来说,也并不浅显哦. ...
- 单例模式:Java单例模式的几种写法及它们的优缺点
总结下Java单例模式的几种写法: 1. 饿汉式 public class Singleton { private static Singleton instance = new Singleton( ...
- 9种Java单例模式详解(推荐)
单例模式的特点 一个类只允许产生一个实例化对象. 单例类构造方法私有化,不允许外部创建对象. 单例类向外提供静态方法,调用方法返回内部创建的实例化对象. 懒汉式(线程不安全) 其主要表现在单例类在外 ...
- 你真的理解了java单例模式吗?讲别人都忽略的细节!
前言:老刘这篇文章敢做保证,java的单例模式讲的比大多数的技术博客都要好,讲述别人技术博客都没有的细节!!! 1 java单例模式 直接讲实现单例模式的两种方法:懒汉式和饿汉式,单例模式的概念自己上 ...
随机推荐
- Django-Model模型Field选项中null和blank的区别
- Option-**blank**设置为True时代表填写表单的时候可以为空,即在save()执行时此字段可以没有,如果字段没有就在数据库上存储一个空字符串: - Option-**null**设置 ...
- JavaScript 手写 setTimeout 及 同步调用和异步调用
demo let timeout = (sec, num) => { const now = new Date().getTime() // 获取进入方法时的时间 let flag = true ...
- STM32 重启之后程序丢失
1 BOOT1 BOOT0都已经接10K接地,晶振波形正常 2 在主程序最开始运行的地方加入5秒的延时,程序不会丢失.原因可能为单片机其它外设没有准备好 int main(void) { delay_ ...
- 读取topic数据存储到文件内
基于python3.6 from pykafka import KafkaClient import logging logging.basicConfig(level=logging.INFO) d ...
- JavaScript学习系列博客_19_JavaScript中方法(method)
方法 - 听了不少调用什么什么方法,所以方法究竟是啥东西? - 如果一个函数作为一个对象的属性保存,那么我们称这个函数时这个对象的方法,调用这个函数就说调用对象的方法(method). 创建一个对象 ...
- Mybatis_day1
mybatis框架的引入: JDBC里面 对功能实现的进一步封装,可以按照以下方式操作 Student s = new Student(id,name); util.save(s); ORM: obj ...
- git 常规操作 windows版
首先在本地建立好文件夹,然后初始化git仓库: git init 接下来在github上面克隆项目: git clone 这里写你的项目地址 然后就可以修改,删除,提交代码了 如果需要在新分支上面开 ...
- 仿京东BOE官网 jQuery代码
$(function() { $("#chanping").mouseenter(function() { $("#column").slideDown(500 ...
- 多元线性回归检验t检验(P值),F检验,R方等参数的含义
做线性回归的时候,检验回归方程和各变量对因变量的解释参数很容易搞混乱,下面对这些参数进行一下说明: 1.t检验:t检验是对单个变量系数的显著性检验 一般看p值: 如果p值小于0.05表示该自 ...
- Soft-to-Hard Vector Quantization for End-to-End Learning Compressible Representations
郑重声明:原文参见标题,如有侵权,请联系作者,将会撤销发布! Abstract: 我们提出了一种新的方法,通过端到端的训练策略来学习深度架构中的可压缩表征.我们的方法是基于量化和熵的软(连续)松弛,我 ...