设计模式 (六)单例模式(Singleton)

本文介绍了单例模式的原理及应用,通过实例展示了如何利用单例模式实现一个日志工具,确保日志不会多次初始化并覆盖,实现尾加功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

单例模式就是确保一个类只有一个实例,并且该实例必须自动创建,并向整个系统提供该实例。这样保证了对外的对象的属性等均为一个实例,就像是银行取款

单例模式原理图:


单例模式分为饿汉式单例模式和懒汉式单例模式。

饿汉式单例模式代码

  1. package com.designpattern.singleton;  
  2.   
  3. public class HungrySingleton {  
  4.     private HungrySingleton() {  
  5.     }  
  6.   
  7.     private static HungrySingleton instance = new HungrySingleton();  
  8.   
  9.     public static HungrySingleton getInstance() {  
  10.         return instance;  
  11.     }  
  12. }  

懒汉式单例模式代码
  1. package com.designpattern.singleton;  
  2.   
  3. public class LazySingleton {  
  4.     private LazySingleton() {  
  5.   
  6.     }  
  7.   
  8.     private static LazySingleton instance = null;  
  9.   
  10.     public static synchronized LazySingleton getInstance() {  
  11.         if (instance == null) {  
  12.             instance = new LazySingleton();  
  13.         }  
  14.         return instance;  
  15.     }  
  16. }  
事例中对于饿汉式是不管外部有没有调用都new出了一个对象,二懒汉式只有在外部调用的时候,并且是第一次的时候才new出来对象的,但是一定要放在sychronized下面

这样感觉像是懒汉模式是在调用的时候分配空间初始化instance的,但是自己感觉上面两种是一样的效果

在实例化先后顺寻的角度分析:饿汉式类内static 对象会在构造器调用前初始化,也就是最先初始化,但是只初始化一次就不再初始化了,毕竟是static的,在构造器之前也就是只有在使用这个类的时候,才实例化。同时后者懒汉式中私有的static也会在构造器之前初始化,但是是null,这样就在第一次调用这个类的时候对instance进行了初始化,但是之后就不会再初始化了,原因也是static,这样看起来对于懒汉和饿汉的初始化和构造过程是一样的。

不过说道具体的地方饿汉式是在加载类的时候创建对象,而懒汉式是在调用getInstance时候创建对象,那么这样他们在创建的时候还是用一定的区别的。

下面借着这个Singleton模式简单的做了一个Log工具,实现对于一个日志工具不会多次初始化,并且不会覆盖掉而是尾加,Log4j就是一个Singleton的实例

  1. package com.designpattern.singleton;  
  2.   
  3. import java.io.File;  
  4. import java.io.FileWriter;  
  5. import java.io.IOException;  
  6. import java.io.PrintWriter;  
  7. import java.text.SimpleDateFormat;  
  8. import java.util.Date;  
  9.   
  10. public class Log {  
  11.   
  12.     private static final String DefalutLogFilePathName = System  
  13.             .getProperty("user.dir")  
  14.             + File.separator + "user.log";  
  15.     private static Log log;  
  16.     private static PrintWriter pw;  
  17.   
  18.     private Log() {  
  19.         pwinit();  
  20.     }  
  21.   
  22.     public static synchronized void log(String message) {  
  23.         if (log == null || pw == null) {  
  24.             log = new Log();  
  25.         }  
  26.         if (pw != null) {  
  27.             pw.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")  
  28.                     .format(new Date())  
  29.                     + " : " + message);  
  30.         }  
  31.     }  
  32.   
  33.     public static void destroy() {  
  34.         log = null;  
  35.         if (pw != null) {  
  36.             pw.close();  
  37.         }  
  38.     }  
  39.   
  40.     private void pwinit() {  
  41.         if (pw == null) {  
  42.             try {  
  43.                 pw = new PrintWriter(new FileWriter(DefalutLogFilePathName,  
  44.                         true), true);  
  45.             } catch (IOException e) {  
  46.                 e.printStackTrace();  
  47.                 pw = null;  
  48.             }  
  49.         }  
  50.     }  
  51. }  

在Client端对Log进行操作,不会覆盖结果,测试成功
  1. package com.designpattern.singleton;  
  2.   
  3. public class Client {  
  4.     public static void main(String[] args) {  
  5.   
  6.         System.out.print("Log");  
  7.         System.out.print("\t");  
  8.         Log.log("start");  
  9.         Log.log("middle");  
  10.         Log.log("end");  
  11.         Log.destroy();  
  12.   
  13.     }  
  14. }  
输出结果:
  1. 2012-04-05 14:46:15 : start  
  2. 2012-04-05 14:46:15 : middle  
  3. 2012-04-05 14:46:15 : end  
在单例模式中,客户调用类的实例时,只能调用一个公共接口,这就是为整个开发团队提供了共享的概念。

但是但是模式的类在实例化以后,不能被别的类继承,在分布式系统中,当系统中的单例类被复制运行在多个虚拟机下时,在每一个虚拟机下都会创建一个实例对象,此时如果想知道具体那个虚拟机下运行着哪个单例对象是很困难的,而且单例类很难实现序列化。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值