黑马程序员之单例模式学习

android培训  java培训 期待与您交流!

java中单例模式是一种常见的设计模式,单例模式分三种:懒汉式单例、饿汉式单例、登记式单例三种。
  单例模式有一下特点:
  1、单例类只能有一个实例。
  2、单例类必须自己自己创建自己的唯一实例。
  3、单例类必须给所有其他对象提供这一实例。
  单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。这些应用都或多或少具有资源管理器的功能。每台计算机可以有若干个打印机,但只能有一个Printer Spooler,以避免两个打印作业同时输出到打印机中。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。总之,选择单例模式就是为了避免不一致状态,避免政出多头。

首先看一个经典的单例实现。

public class Singleton {

private static Singleton uniqueInstance = null;

private Singleton() {

// Exists only to defeat instantiation.

}

public static Singleton getInstance() {

if (uniqueInstance == null) {

uniqueInstance = new Singleton();

}

return uniqueInstance;

}

// Other methods...

}

Singleton通过将构造方法限定为private避免了类在外部被实例化,在同一个虚拟机范围内,Singleton的唯一实例只能通过getInstance()方法访问。(事实上,通过Java反射机制是能够实例化构造方法为private的类的,那基本上会使所有的Java单例实现失效。此问题在此处不做讨论,姑且掩耳盗铃地认为反射机制不存在。)

但是以上实现没有考虑线程安全问题。所谓线程安全是指:如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。或者说:一个类或者程序所提供的接口对于线程来说是原子操作或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题。显然以上实现并不满足线程安全的要求,在并发环境下很可能出现多个Singleton实例。

 1 public class TestStream { 2     private String name; 3     public String getName() { 4         return name; 5     } 6     public void setName(String name) { 7         this.name = name; 8     }  9     //该类只能有一个实例10     private TestStream(){}    //私有无参构造方法11     //该类必须自行创建12     //有2种方式13     /*private static final TestStream ts=new TestStream();*/14     private static TestStream ts1=null;15     //这个类必须自动向整个系统提供这个实例对象16     public static TestStream getTest(){17         if(ts1==null){18             ts1=new TestStream();19         }20         return ts1;21     }22     public void getInfo(){23         System.out.println("output message "+name);24     }25 }

 1 public class TestMain { 2     public static void main(String [] args){ 3         TestStream s=TestStream.getTest(); 4         s.setName("张孝祥"); 5         System.out.println(s.getName()); 6         TestStream s1=TestStream.getTest(); 7         s1.setName("张孝祥"); 8         System.out.println(s1.getName()); 9         s.getInfo();10         s1.getInfo();11         if(s==s1){12             System.out.println("创建的是同一个实例");13         }else if(s!=s1){14             System.out.println("创建的不是同一个实例");15         }else{16             System.out.println("application error");17         }18     }19 }

运行结果:
  张孝祥
  张孝祥
  output message 张孝祥
  output message 张孝祥
  创建的是同一个实例

结论:由结果可以得知单例模式为一个面向对象的应用程序提供了对象惟一的访问点,不管它实现何种功能,整个应用程序都会同享一个实例对象。

1.饿汉式单例类

 1 //饿汉式单例类.在类初始化时,已经自行实例化  2 public class Singleton1 { 3     //私有的默认构造子 4     private Singleton1() {} 5     //已经自行实例化  6     private static final Singleton1 single = new Singleton1(); 7     //静态工厂方法  8     public static Singleton1 getInstance() { 9         return single;10     }11 }

2.懒汉式单例类

 1 //懒汉式单例类.在第一次调用的时候实例化  2 public class Singleton2 { 3     //私有的默认构造子 4     private Singleton2() {} 5     //注意,这里没有final     6     private static Singleton2 single=null; 7     //静态工厂方法  8     public synchronized  static Singleton2 getInstance() { 9          if (single == null) {  10              single = new Singleton2();11          }  12         return single;13     }14 }

3.登记式单例类

 1 import java.util.HashMap; 2 import java.util.Map; 3 //登记式单例类. 4 //类似Spring里面的方法,将类名注册,下次从里面直接获取。 5 public class Singleton3 { 6     private static Map<String,Singleton3> map = new HashMap<String,Singleton3>(); 7     static{ 8         Singleton3 single = new Singleton3(); 9         map.put(single.getClass().getName(), single);10     }11     //保护的默认构造子12     protected Singleton3(){}13     //静态工厂方法,返还此类惟一的实例14     public static Singleton3 getInstance(String name) {15         if(name == null) {16             name = Singleton3.class.getName();17             System.out.println("name == null"+"--->name="+name);18         }19         if(map.get(name) == null) {20             try {21                 map.put(name, (Singleton3) Class.forName(name).newInstance());22             } catch (InstantiationException e) {23                 e.printStackTrace();24             } catch (IllegalAccessException e) {25                 e.printStackTrace();26             } catch (ClassNotFoundException e) {27                 e.printStackTrace();28             }29         }30         return map.get(name);31     }32     //一个示意性的商业方法33     public String about() {    34         return "Hello, I am RegSingleton.";    35     }    36     public static void main(String[] args) {37         Singleton3 single3 = Singleton3.getInstance(null);38         System.out.println(single3.about());39     }40 }

黑马程序员之单例模式学习

时间: 2024-10-08 18:52:40

黑马程序员之单例模式学习的相关文章

黑马程序员——c语言学习心得—— 指针

黑马程序员——c语言学习心得—— 指针 -------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- 1,打开文件fopen(文件名,打开方式);例如:FILE *fp = fopen("a1","r");  返回的是文件的指针(文件在内存缓冲区的首地址)fopen函数反回值 是指向 a1文件的指针,通常赋值给一个指针变量关于文件名a1 也可以是一个“路径+文件名”   c:\abc.txt----------------

黑马程序员——c语言学习心得——位运算符

黑马程序员——c语言学习心得——位运算符 -------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- 位运算符C语言提供了六种位运算符: & 按位与 | 按位或 ^ 按位异或 ~ 取反 << 左移 >> 右移 1. 按位与运算 按位与运算符"&"是双目运算符.其功能是参与运算的两数各对应的二进位相与.只有对应的两个二进位均为1时,结果位才为1 ,否则为0.参与运算的数以补码方式出现. 例如:9&

黑马程序员——c语言学习心得—— 电影购票系统

黑马程序员——c语言学习心得——  电影购票系统 -------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- 1,基础信息类 #import <Foundation/Foundation.h> #import "Cinema.h" void buyMovieTicket(){ //购买电影票 //调用 电影院的类的 buyTicket的方法 //                  [[Cinema alloc] init]; C

黑马程序员——c语言学习心得——函数传递二维数组

黑马程序员——c语言学习心得——函数传递二维数组 -------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- 一.定义指针的时候一定要初始化.   变量定义的时候给变量初始化,这是保证不出错的一个很好的习惯.尤其是在指针的使用上,如果我们没有给指针初始化,就会出现野指针,该指针的指向并不是我们所希望的,一旦错误的释放了这个指针,就会发生内存的访问.那么如何初始化指针变量呢,一般有以下几种方法:   1.初始化空指针   int* pInteger=N

黑马程序员——oc语言学习心得—— 属性声明和赋值

黑马程序员——oc语言学习心得—— 属性声明和赋值 -------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- 1,在oc中所有类继承与终极父类Object2,声明字符变量采用N是string  *_xxx 实例变量一般以下划线开头3,在oc中方法以+ -号区分 -号开头是实例方法或对象方法  +号开头是类方法  前置用对象调用 后者用类名调用4,在xcode4以后声明@property 不用在写@snysize  自动生成get.set方法5,属性

黑马程序员——c语言学习心得——位运算符a

黑马程序员——c语言学习心得——位运算符 -------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- c语言中经常需要通过函数传递二维数组,有三种方法可以实现,如下: 方法一, 形参给出第二维的长度. 例如: #include <stdio.h>void func(int n, char  str[ ][5] ){int i;for(i = 0; i < n; i++)  printf("/nstr[%d] = %s/n"

黑马程序员—oc语言学习心得—block

黑马程序员—oc语言学习心得—block -------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- 1,程序一般用typedef定义一个block类型 然后调用 typedef void(^xxx)(); 可使用助记符 inlineblock 2,Block内部可以访问外部变量:默认情况下,Block内部不能修改外部的局部变量 给局部变量加上__block关键字,则这个局部变量可以在block内部进行修改. 3,block的作用: 利用block封

黑马程序员——oc语言学习心得----NSFileManager

黑马程序员——oc语言学习心得----NSFileManager -------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- 自己总结了下NSFileManager的简单几个用法 : // 首先要创建文件管理器 NSFileManager *fm=[NSFileManager defaultManager]; 1,判断文件是否存在 fileExistsAtPath: 2,判断文件是否是目录 fileExistsAtPath: 3, 判断文件是否可读

黑马程序员--java基础学习笔记5

黑马程序员--java基础学习笔记6 一.笔记内容概述: 数组-第二种定义格式.数组-常见操作-遍历-最值-选择排序-冒泡排序-排序位置置换代码提取.数组-排序的性能问题.数组-常见功能-查找-折半查找.进制转换-查表法-整合. 二.常用内容介绍: 1.数组初始化的三种方式: int[] arr = new int[3]; int[] arr = new int[]{1,2,3}; int[] arr = {1,2,3}; 2.查表法: 如果数据中出现了对应关系,而且对应关系的一方是有序的数字编