Inspired by Effective Java.
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 SigletonHolder {
private static final instance = new MaYun();
}
public static final getInstance() {
return SigletonHolder.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开始支持;无偿提供序列化机制,绝对防止多次实例化,即使在面对复杂的序列化或者反射攻击的时候。
JDK 1.5引入枚举,通过枚举,我们也可以实现单例模式,不仅仅是单例哦,以下是稍微拓展之后的代码:
public enum SingletonEnum
{
INSTANCE01,INSTANCE02;
private String name;
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
}
测试
public class Test
{
public static void main(String[] args)
{
SingletonEnum instance01=SingletonEnum.INSTANCE01;
instance01.setName("terje");
System.out.println(instance01.getName()); SingletonEnum instance02=SingletonEnum.INSTANCE01;
System.out.println(instance02.getName()); SingletonEnum instance03=SingletonEnum.INSTANCE02;
instance03.setName("liu");
System.out.println(instance03.getName()); SingletonEnum instance04=SingletonEnum.INSTANCE02;
instance04.setName("liu");
System.out.println(instance04.getName()); } }
输出:
terje
terje
liu
liu
总之,五类:懒汉,恶汉,双重校验锁,静态内部类,枚举。
恶汉:因为加载类的时候就创建实例,所以线程安全(多个ClassLoader存在时例外)。缺点是不能延时加载。
懒汉:需要加锁才能实现多线程同步,但是效率会降低。优点是延时加载。
双重校验锁:麻烦,在当前Java内存模型中不一定都管用,某些平台和编译器甚至是错误的,因为instance = new MaYun()这种代码在不同编译器上的行为和实现方式不可预知。
静态内部类:延迟加载,减少内存开销。因为用到的时候才加载,避免了静态field在单例类加载时即进入到堆内存的permanent代而永远得不到回收的缺点(大多数垃圾回收算法是这样)。
枚举:很好,不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。但是失去了类的一些特性,没有延迟加载,用的人也太少了~~
以后多推广推广单元素枚举这种更好的单例实现方式。在项目中的代码开始修改实施
枚举类型的单例模式(java)的更多相关文章
-
2基本类型数组和枚举类型——重拾Java
2.1 标识符和关键字 2.1.1标识符 标识符:用来标志类名.变量名.方法名.类型名.数组名.文件名的有效字符序列称为标识符.简单地说,标识符就是一个名字. Java关于标识符的语法规则 标识符由字 ...
-
java枚举类型构造方法为什么是private的
枚举类型是单例模式的.你需要实例化一次,然后再整个程序之中就可以调用他的方法和成员变量了.枚举类型使用单例模式是因为他的值是固定的,不需要发生改变.更多知识见 http://blog.yemou.ne ...
-
java枚举类型
jvm并不支持枚举类型,java中枚举类型是在编译器层面上实现的,先看如下代码: package demo.nio; public class EnumDemo { public static enu ...
-
单例模式——Java EE设计模式解析与应用
单例模式 目录: 一.何为单例 二.使用Java EE实现单例模式 三.使用场景 一.何为单例 确保一个类只有一个实例,并且提供了实例的一个全局访问点 1.1 单例模式类图 ...
-
单例模式——java设计模式
单例模式 目录: 一.何为单例 二.使用Java EE实现单例模式 三.使用场景 一.何为单例 确保一个类只有一个实例,并且提供了实例的一个全局访问点 1.1 单例模式类图 ...
-
测者的测试技术手册:Junit单元测试遇见的一个枚举类型的坑(枚举类型详解)
Enum的简介 枚举类型很早就在计算机语言中存在了,主要被用来将一组相似的值包含进一种类型中,这种类型的名称被定义成独一无二的类型描述符,这就是枚举类型. 在java语言中,枚举类型是一个完整功能的类 ...
-
深度分析 Java 的枚举类型:枚举的线程安全性及序列化问题(转)
写在前面: Java SE5 提供了一种新的类型 Java的枚举类型,关键字 enum 可以将一组具名的值的有限集合创建为一种新的类型,而这些具名的值可以作为常规的程序组件使用,这是一种非常有用的功能 ...
-
深度分析Java的枚举类型—-枚举的线程安全性及序列化问题
原文:深度分析Java的枚举类型--枚举的线程安全性及序列化问题 枚举是如何保证线程安全的 要想看源码,首先得有一个类吧,那么枚举类型到底是什么类呢?是enum吗?答案很明显不是,enum就和clas ...
-
java基础(十一) 枚举类型
枚举类型Enum的简介 1.什么是枚举类型 枚举类型: 就是由一组具有名的值的有限集合组成新的类型.(即新的类). 好像还是不懂,别急,咱们先来看一下 为什么要引入枚举类型 在没有引入枚举类型前,当我 ...
随机推荐
-
TCL:使用、添加库文件
>直接引用工具自带的库文件 通过指令: .1查看能直接调用的库文件路径 #可以查到工具默认库文件路径,一般包括回显中的路径以及回显中路径的父路径. info library #D:/Script ...
-
SpringMVC 基于注解的Controller详解
本文出处 http://blog.csdn.net/lufeng20/article/details/7598801 概述 继 Spring 2.0 对 Spring MVC 进行重大升级后,Spri ...
-
Spark快速数据处理
原书名:Fast Data Processing with Spark 原出版社:Packt Publishing 作者: (美)Holden Karau 丛书名:大数据技术丛书 出版社:机械工业出版 ...
-
【Android】实现动态显示隐藏密码输入框的内容
在设置输入密码框时,有些时候需要按钮控制输入的是“明文”或者“暗文”. 这里提供一种Android实现动态显示隐藏密码输入框的内容的方法: 主要是通过设置EditText的setTransformat ...
-
WEP无线加密破解
工具:Aircrack套件(airmon-ng.airodump-ng.aireplay-ng) 带有套件的操作系统:KaLi Linux.BackTrack.Beini(奶瓶)...等 1.开启无线 ...
-
verilog学习笔记(4)_有限状态机
有限状态机: 有限状态机是由寄存器组和组合逻辑构成的硬件时序电路: - 其状态(即由寄存器组的1和0的组合状态所构成的有限个状态)只能在同一时钟跳变沿的情况下才能从一个状态转向另一个状态: - 究竟转 ...
-
Linux中Root密码破解
1.开机后在选择菜单时按下e进入编辑模式 2.选择linux16这一行,在行末尾添加 rd.break 3.然后Ctrl+x执行.然后进入shell界面: 4.设置密码: 1.重新挂载根目录为读写模式 ...
-
迭代和JDB调试
迭代和JDB调试 题目要求 1 使用C(n,m)=C(n-1,m-1)+C(n-1,m)公式进行递归编程实现求组合数C(m,n)的功能 2 m,n 要通过命令行传入 3 提交测试运行截图(至少三张:正 ...
-
ES6 Proxy的应用场景
一.相关API Proxy Reflect 二.Proxy应用场景 1.数据校验 表单提交的时候做数据校验,例如年龄是不是满足条件,数据类型是不是满足要求等等,这场场景非常适合使用Proxy. 下面展 ...
-
Winter Storm Warning
2019-01-02 11:22:33 ...WINTER STORM WARNING REMAINS IN EFFECT UNTIL 6 AM AKST TUESDAY... * WHAT...He ...