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

一、创建单例对象

懒汉式

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. C#LeetCode刷题之#53-最大子序和(Maximum Subarray)

    问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/4012 访问. 给定一个整数数组 nums ,找到一个具有最大和的 ...

  2. Vue 生成条形码 jsbarcode的使用

    安装依赖包 npm install jsbarcode --save 在使用页面引入依赖包 import JsBarcode from 'jsbarcode' 定义img标签和id <img i ...

  3. 国人开源了一款小而全的 Java 工具类库,厉害啊!!

    最近栈长看到了一款小而全的 Java 工具类库:Hutool,Github 已经接近 14K Star 了,想必一定很优秀,现在推荐给大家,很多轮子不要再造了! Hutool 是什么 Hutool 是 ...

  4. Android 获取对象列表中的某一列 / 所有对象的某一字段,Realm数据库可获取某一字段所有值

    现在项目用的数据库是Realm,所以想要获取数据库中某一字段的数据没有一句直接的语句进行获取,就像MySQL一样的select name from User,从User表里获取所有的name. 所以只 ...

  5. Url跳转漏洞常见

    Url跳转漏洞常见出现点: 1.用户登录.统一身份认证处,认证完后会跳转. 2.用户分享.收藏内容过后,会跳转. 3.跨站点认证.授权后,会跳转. 4.站内点击其它网址链接时,会跳转. Url跳转漏洞 ...

  6. git存储用户名和密码

    git config --global credential.helper store 输入一次用户名和密码后,git会自动记录用户名密码

  7. Magento1.9 add attribute to catalog product & assign to all attribute set general group

    $installer = $this; $attributes = array( 'region' => array( 'type' => 'int', 'input' => 'se ...

  8. 2020.5.21 第一篇 Scrum冲刺博客

    Team:银河超级无敌舰队 Project:招新通 项目冲刺集合贴:链接 目录 一.Alpha 阶段成员任务安排 二.明日任务安排 三.预期的任务量 四.敏捷开发前的感想 五.团队期望 一.Alpha ...

  9. 第6篇 Scrum 冲刺博客

    1.站立会议 照骗 进度 成员 昨日完成任务 今日计划任务 遇到的困难 钟智锋 重构游戏逻辑代码 改写部分客户端代码,制作单机版 庄诗楷 进行了相关的装饰改进 与其他部分合成完成游戏 合成遇到bug, ...

  10. 服务器基本配置(ubuntu)

    服务器基本配置(ubuntu) 学习目标: 修改初始服务器名字(ubuntu 16.04 ) 修改初始服务器名字(ubuntu 18.04 ) ubuntu换源 更改默认python版本 安装软件出现 ...