单例模式是一种设计模式,是在整个运行过程中只需要产生一个实例。那么怎样去创建呢,以下提供了几种方案。

一、创建单例对象

懒汉式

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单例模式的实现与破坏的更多相关文章

  1. 用java单例模式实现面板切换

    1.首先介绍一下什么是单例模式: java单例模式是一种常见的设计模式,那么我们先看看懒汉模式: public class Singleton_ { //设为私有方法,防止被外部类引用或实例 priv ...

  2. 【深入】java 单例模式(转)

    [深入]java 单例模式 关于单例模式的文章,其实网上早就已经泛滥了.但一个小小的单例,里面却是有着许多的变化.网上的文章大多也是提到了其中的一个或几个点,很少有比较全面且脉络清晰的文章,于是,我便 ...

  3. 深入Java单例模式(转)

    深入Java单例模式 源自 http://devbean.blog.51cto.com/448512/203501 在GoF的23种设计模式中,单例模式是比较简单的一种.然而,有时候越是简单的东西越容 ...

  4. Java 单例模式的七种写法

    Java 单例模式的七种写法 第一种(懒汉,线程不安全) public class Singleton { private static Singleton instance; private Sin ...

  5. java单例模式之懒汉式分析

    转自:http://blog.csdn.net/withiter/article/details/8140338 今天中午闲着没事,就随便写点关于Java单例模式的.其实单例模式实现有很多方法,这里我 ...

  6. Java 单例模式探讨

    以下是我再次研究单例(Java 单例模式缺点)时在网上收集的资料,相信你们看完就对单例完全掌握了 Java单例模式应该是看起来以及用起来简单的一种设计模式,但是就实现方式以及原理来说,也并不浅显哦. ...

  7. 单例模式:Java单例模式的几种写法及它们的优缺点

    总结下Java单例模式的几种写法: 1. 饿汉式 public class Singleton { private static Singleton instance = new Singleton( ...

  8. 9种Java单例模式详解(推荐)

    单例模式的特点 一个类只允许产生一个实例化对象. 单例类构造方法私有化,不允许外部创建对象. 单例类向外提供静态方法,调用方法返回内部创建的实例化对象.  懒汉式(线程不安全) 其主要表现在单例类在外 ...

  9. 你真的理解了java单例模式吗?讲别人都忽略的细节!

    前言:老刘这篇文章敢做保证,java的单例模式讲的比大多数的技术博客都要好,讲述别人技术博客都没有的细节!!! 1 java单例模式 直接讲实现单例模式的两种方法:懒汉式和饿汉式,单例模式的概念自己上 ...

随机推荐

  1. Eclipse怎么调整字体大小和主题

    调整字体 哈哈哈哈哈 ( ̄▽ ̄),直接Ctrl + -/+号,....在英文输入法状态. 或者,你想更细致的调整字体类型,可以在Winodw -> Preferences中调整: 换主题 你可以 ...

  2. 个性探测综述阅读笔记——Recent trends in deep learning based personality detection

    目录 abstract 1. introduction 1.1 个性衡量方法 1.2 应用前景 1.3 伦理道德 2. Related works 3. Baseline methods 3.1 文本 ...

  3. PHP 开发工程师基础篇 - PHP 字符串

    字符串 (String) 字符串是一系列字符的集合.如 “abc”. 在 PHP 中,一个字符代表一个字节,一个字节 (Byte) 有 8 比特 (bit). PHP 仅支持 256 字符集,因此 P ...

  4. Kettle基本使用

    Kettle基本使用 Kettle的几个子程序的功能和启动方式 Spoon.bat 图形界面方式启动作业和转换设计器. Pan.bat 命令行方式执行转换. Kitchen.bat   命令行方式执行 ...

  5. 自动化项目Jenkins持续集成

    一.Jenkins的优点 1.传统网站部署流程   一般网站部署的流程 这边是完整流程而不是简化的流程 需求分析—原型设计—开发代码—内网部署-提交测试—确认上线—备份数据—外网更新-最终测试 ,如果 ...

  6. Android中RecyclerView用法,一步一步教你如何使用RecyclerView以及带你走过编码中可能会出现的坑~

    首先,要明白RecyclerView是做什么的?其次是为什么要用RecyclerView?这里牵扯到RecyclerView和ListView的区别,这里不废话,大家自行百度即可! 以下示例我用的An ...

  7. 《Head First 设计模式》:外观模式

    正文 一.定义 外观模式提供了一个统一的接口,用来访问子系统中的一群接口.外观定义了一个高层接口,让子系统更容易使用. 要点: 外观模式将一个或数个类的复杂的一切都隐藏在背后,只显露出一个干净美好的外 ...

  8. 数组的三种方式总结 多维数组的遍历 Arrays类的常用方法总结

    一.数组的三种声明方式总结 public class WhatEver { public static void main(String[] args) { //第一种 例: String[] tes ...

  9. 目标追踪(Object Tracking)概念的简要介绍

    现在我们有一个视频流,可以拆解出 N 个帧出来,这时候初始帧/某一帧中出现了一个我们感兴趣目标,我们希望在后续帧中对这个目标进行追踪,这时候就需要 CV 中的目标追踪: 目标追踪的效果如下: 虽然效果 ...

  10. vue前端工程化

    今日目标 1.能够了解模块化的相关规范 2.了解webpack3.了解使用Vue单文件组件4.能够搭建Vue脚手架 5.掌握Element-UI的使用 1.模块化的分类 A.浏览器端的模块化   1) ...