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单例模式 直接讲实现单例模式的两种方法:懒汉式和饿汉式,单例模式的概念自己上 ...
随机推荐
- 2020-07-23:开启rdb后,redis的启动流程是怎样的?
福哥答案2020-07-23: Redis 在完成初始化全局服务器配置,加载配置文件,初始化服务器,开始加载持久化的数据到内存中.如果启用了 appendonly 了,则Redis从 appendfi ...
- C#LeetCode刷题之#55-跳跃游戏(Jump Game)
问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/3674 访问. 给定一个非负整数数组,你最初位于数组的第一个位置. ...
- Mac 从睡眠恢复后没有声音的问题
重启能解决,不想重启,因为不想重置当前工作状态 换个办法是: 杀掉coreaudiod进程,然后系统会把他自动拉起,然后就ok了.
- python 常用函数集合
1.常用函数 round() : 四舍五入 参数1:要处理的小数 参数2:可选,如果不加,就是不要小数,如果加,就是保留几位小数 abs() :绝对值 ...
- 基于vue的实时视频流开发
背景:多个实时视频的介入 技术:hls.js的流媒体,支持格式已m3u8为主 解决了什么:多个实时视频长时间播放会有卡顿的情况 具体代码实现: import Hls from 'hls.js' pla ...
- 虚拟化技术之kvm虚拟机创建工具virt-install
在前边的博客中,我们创建KVM虚拟机用到了virt-manager,这个工具是一个图形化工具,创建虚拟机很方便:除此我们还是用virsh define/create +虚拟机配置文件来创建虚拟机,这种 ...
- Spring注解驱动开发04(给容器中注册组件的方式)
给容器中注册组件的方式 1. 组件注解标注 + 包扫描(适用于自己写的类) //控制层组件 @Controller public class PersonController { } //业务逻辑层组 ...
- Ubutun重启网卡
一.network利用root帐户# service networking restart 或者/etc/init.d/networking restart 二.ifdown/ifup# ifdown ...
- 区块链入门到实战(6)之区块链 – 哈希(Hash)
密码学中,最重要的函数之一是哈希函数.哈希函数将任意大小的数据(内容)映射到固定大小的数据(哈希值). 哈希函数是单向的,从内容生成哈希值很容易,但从哈希值映射到内容很难. 比特币使用SHA-256哈 ...
- java23种设计模式——八、组合模式
目录 java23种设计模式-- 一.设计模式介绍 java23种设计模式-- 二.单例模式 java23种设计模式--三.工厂模式 java23种设计模式--四.原型模式 java23种设计模式-- ...