Java读书笔记(8)-单例模式

今天在阅读《Effective Java 2》第3条时,获知一种使用枚举enum实现单例模式的新方法,然而书上并没有就此展开深入说明,于是上网查阅了一些资料,现将收集的资料整理于下:

转自:枚举类型的单例模式:http://callmegod.iteye.com/blog/1474441

Singleton 模式是在编程实践中应用最广泛的几种设计模式之一。以前知道的,实现单例的方法有两种(下面的A、B)。刚刚在读《Effective Java的时候》学到一种新的更好的方法(E):单元素的枚举类型。同时通过网上资料也知道了其他两种方法(C、D)。最后一种在Java中从1.5版本 开始支持,其他语言在验证后说明。

A.饿汉式(类加载的时候就创建实例)。
代码如下:

public class MaYun {
public static final Mayun instance = new Mayun(); //静态的final的MaYun
private MaYun() {
//MaYun诞生要做的事情
}
public void splitAlipay() {
System.out.println(“Alipay是我的啦!看你丫Yahoo绿眉绿眼的望着。。。”);
}
}
Call:MaYun.instance.splitAlipay();

Feature:可以通过反射机制攻击;线程安全[多个类加载器除外]。

A+.饿汉变种[推荐]

public class MaYun {
private static Mayun instance = new Mayun();
private static getInstance() {
return instance;
}
private MaYun() {
//MaYun诞生要做的事情
}
public void splitAlipay() {
System.out.println(“Alipay是我的啦!看你丫Yahoo绿眉绿眼的望着。。。”);
}
}

A++.饿汉变种(类初始化的时候实例化instance):

public class MaYun {
private MaYun instance = null;
static {
instance = new MaYun();
}
private MaYun() {
//MaYun诞生要做的事情
}
public static MaYun getInstance() {
return this.instance;
}
public void splitAlipay() {
System.out.println(“Alipay是我的啦!看你丫Yahoo绿眉绿眼的望着。。。”);
}
}

B.懒汉式。
代码如下:

public class MaYun {
private static MaYun instance = null;
private MaYun() {
//MaYun诞生要做的事情
}
public static MaYun getInstance() {
if (instance == null) {
instance = new MaYun();
}
return instance;
}
public void splitAlipay() {
System.out.println(“Alipay是我的啦!看你丫Yahoo绿眉绿眼的望着。。。”);
}
}
Call:MaYun.getInstance().splitAlipay();

Feature:延时加载;线程不安全,多线程下不能正常工作;需要额外的工作(Serializable、transient、readResolve())来实现序列化。

B+.懒汉式变种。

public class MaYun {
private static MaYun instance = null;
private MaYun() {
//MaYun诞生要做的事情
}
public static synchronized MaYun getInstance() {
if (instance == null) {
instance = new MaYun();
}
return instance;
}
public void splitAlipay() {
System.out.println(“Alipay是我的啦!看你丫Yahoo绿眉绿眼的望着。。。”);
}
}

Feature:线程安全;效率比较低,因为需要线程同步的时候比较少。

C.静态内部类[推荐]。
代码如下:

public class MaYun {
private static class SingletonHolder {
private static final instance = new MaYun();
}
public static final getInstance() {
return SingletonHolder.instance;
}
private MaYun() {
//MaYun诞生要做的事情
}
public void splitAlipay() {
System.out.println(“Alipay是我的啦!看你丫Yahoo绿眉绿眼的望着。。。”);
}
Call:MaYun.getInstance().splitAlipay();

Feature:线程安全;延迟加载。

D.双重校验锁[不推荐]。
代码如下:

public class MaYun {
private volatile static MaYun instance;
private MaYun (){}
public static MaYun getInstance() {
if (instance == null) {
synchronized (MaYun.class) {
if (instance == null) {
instance = new MaYun();
}
}
}
return instance;
}
}

Feature:jdk1.5之后才能正常达到单例效果。

E.编写一个包含单个元素的枚举类型[极推荐]。
代码如下:

public enum MaYun {
himself; //定义一个枚举的元素,就代表MaYun的一个实例
private String anotherField;
MaYun() {
//MaYun诞生要做的事情
//这个方法也可以去掉。将构造时候需要做的事情放在instance赋值的时候:
/** himself = MaYun() {
* //MaYun诞生要做的事情
* }
**/
}
public void splitAlipay() {
System.out.println(“Alipay是我的啦!看你丫Yahoo绿眉绿眼的望着。。。”);
}
}
Call:MaYun.himself.splitAlipay();

Feature:从Java1.5开始支持;无偿提供序列化机制,绝对防止多次实例化,即使在面对复杂的序列化或者反射攻击的时候。

总之,五类:懒汉,饿汉,双重校验锁,静态内部类,枚举。
饿汉:因为加载类的时候就创建实例,所以线程安全(多个ClassLoader存在时例外)。缺点是不能延时加载。
懒汉:需要加锁才能实现多线程同步,但是效率会降低。优点是延时加载。
双重校验锁:麻烦,在当前Java内存模型中不一定都管用,某些平台和编译器甚至是错误的,因为instance = new MaYun()这种代码在不同编译器上的行为和实现方式不可预知。
静态内部类:延迟加载,减少内存开销。因为用到的时候才加载,避免了静态field在单例类加载时即进入到堆内存的permanent代而永远得不到回收的缺点(大多数垃圾回收算法是这样)。
枚举:很好,不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。但是失去了类的一些特性,没有延迟加载,用的人也太少了~~

比较了这么多中单例实现模式,发现使用枚举enum除了没有延迟加载这一点好处之外,无论是编程方便程度和安全性都是最后的,以后尝试在自己开发中使用这种新的方法。

时间: 2024-11-06 02:09:20

Java读书笔记(8)-单例模式的相关文章

think in java 读书笔记

java中没有单独函数的概念,依赖类的方法. java中优化了向前引用,类可以在调用者之后. java中包的命名方法实际上是网址的倒转. c++中因为存在全局变量和函数所以会存在一个变量名冲突的问题,但是java中不存在全局变量,不同程序设计者通过不同的类将相同名字的变量和方法隔离. static关键字 通常,我们创建类时会指出那个类的对象的外观与行为.除非用new 创建那个类的一个对象,否则实际上并 未得到任何东西.只有执行了new 后,才会正式生成数据存储空间,并可使用相应的方法. 但在两种

Effective Java 读书笔记(2创建和销毁对象)

第一章是引言,所以这里不做笔记,总结一下书中第一章的主要内容是向我们解释了这本书所做的事情:指导Java程序员如何编写出清晰.正确.可用.健壮.灵活和可维护的程序. 2.1考虑用静态工厂方法代替构造器 静态工厂方法与构造器相比有四大优势: (1)静态工厂方法有名称,具有适当名称的静态工厂方法易于使用.易于阅读: (2)不必每次在调用它们的时候都创建一个新的对象: (3)可以返回原返回类型的任何子类型的对象: (4)在创建参数化类型实例的时候,它们使代码变得更加简洁. 同时静态工厂方法也有两大缺点

Effective Java读书笔记(4 类和接口)

4.1 使类和成员的可访问性最小化 要区别设计良好的模块和设计不好的模块,最重要的因素在于,这个模块对于外部的其他模块而言,是否隐藏其内部数据和其他实现细节.设计良好的模块会隐藏所有的实现细节,把它的API与它的实现清晰的隔离开来,然后模块之间只通过API进行通信,一个模块不需要知道其他模块内部的工作情况,这个概念被称为信息隐藏或封装,是软件设计的基本原则之一. 4.2 在公有类中使用访问方法而非公有域 坚持面向对象程序设计思想:如果类可以在它所在的包的外部进行访问,就提供访问方法,以保留将来改

【java读书笔记】——java的异常处理

程序在实际环境的运行过程中,安全成为需要首先考虑的重要因素之一,这也是用户和程序员最关心的问题.同时,Java语言健壮性也体现在了能够及时有效地处理程序中的错误,准确的说是Java的异常处理机制为解决安全问题提交了一份满意的答卷. 先从整体上看一下本文要介绍的内容,然后进行详细的分析: 1.异常的概念 异常又称为例外,是特殊的运行错误对象,对应着Java语言特定的运行错误处理机制. 上面的概念是书本上说的,下面我谈一下自己的看法 其实异常处理机制提供了统一的机制来识别和响应程序错误,异常可以为我

Effective Java读书笔记(3对于所有对象都通用的方法)

3.1 覆盖equals时请遵守通用约定 什么时候应该覆盖Object.equals()方法呢? 如果类具有自己特有的"逻辑相等"概念(不同于对象等同的概念),而且超类还没有覆盖equals以实现期望的行为,这时我们就需要覆盖equals方法. Object.equals()方法具有自反性.对称性.传递性.一致性和与null比较返回false的特点. 实现高质量equals方法的诀窍: (1)使用==操作符检查"参数是否为这个对象的引用".如果是,则返回true,这

think in java 读书笔记 3 —— 数据报

目录 think in java 读书笔记 1 ——移位 think in java 读书笔记 2 —— 套接字 概要 1. 数据报基本知识 1. 数据报基本知识 之前套接字中例子使用的都是“传输控制协议”(TCP),亦称作“基于数据流的套接字”.根据该协议的设计宗旨,它具有高度的可靠性,而且能保证数据顺利抵达目的地.换言之,它允许重传那些由于各种原因半路“走失”的数据.而且收到字节的顺序与它们发出来时是一样的.当然,这种控制与可靠性需要我们付出一些代价:TCP 具有非常高的开销. 还有另一种协

【java读书笔记】——java开篇宏观把控 + HelloWorld

学完java有一段时间了,一直没有做相应的总结,总觉得有一种缺憾.从这篇博客开始,将自己平时的学习笔记进行总结归纳,分享给大家. 这篇博客主要简单的介绍一下java的基础知识,主要的目的是扫盲.原来只是听说过java,现在自己真正开始学习了,看完了第一集视频之后,问自己,你知道java是什么吗?问完之后才发现一无所知,上完查一查,简单了解一下,给自己扫盲. 介绍 知道java是一种可跨平台应用软件的面向对象的程序设计语言,就可以了. 组成 Java由四方面组成:java编程语言,java类文件格

大话设计模式读书笔记2——单例模式

单例模式是一种常用的软件设计模式.在它的核心结构中只包含一个被称为单例类的特殊类.通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源.如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案. 我们来看一下单例模式下的饿汉模式代码: /// <summary> /// 饿汉模式 /// </summary> public class Singleton { //私有的构造器 private Singleton() {

【java读书笔记】JSTL,快速精通

JSTL并不是什么新颖的技术而且很简单,甚至有人认为JSTL已经过时了.但是我认为它既然存在,就有存在的道理,作为技术人员就应该知道它们是什么,怎么使用,有什么优点. JSTL包含两部分:标签库和EL语言.下面先介绍标签库,再介绍EL语言,最后总结一下JSTL的优点. JSTL JSTL简介 JSTL(JavaServer Page Standard Tag Library,即:JSP标准标签库),是由SUN公司提供的简化JSP页面设计的标签.JSTL是由Core(核心库).Format(格式化