(六)单例模式(Singleton)
最后更新于:2022-04-01 15:48:16
单例模式就是确保一个类只有一个实例,并且该实例必须自动创建,并向整个系统提供该实例。这样保证了对外的对象的属性等均为一个实例,就像是银行取款
单例模式原理图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-07-12_578455cce0a88.png)
单例模式分为饿汉式单例模式和懒汉式单例模式。
饿汉式单例模式代码
~~~
package com.designpattern.singleton;
public class HungrySingleton {
private HungrySingleton() {
}
private static HungrySingleton instance = new HungrySingleton();
public static HungrySingleton getInstance() {
return instance;
}
}
~~~
懒汉式单例模式代码
~~~
package com.designpattern.singleton;
public class LazySingleton {
private LazySingleton() {
}
private static LazySingleton instance = null;
public static synchronized LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
~~~
事例中对于饿汉式是不管外部有没有调用都new出了一个对象,二懒汉式只有在外部调用的时候,并且是第一次的时候才new出来对象的,但是一定要放在sychronized下面
这样感觉像是懒汉模式是在调用的时候分配空间初始化instance的,但是自己感觉上面两种是一样的效果
在实例化先后顺寻的角度分析:饿汉式类内static 对象会在构造器调用前初始化,也就是最先初始化,但是只初始化一次就不再初始化了,毕竟是static的,在构造器之前也就是只有在使用这个类的时候,才实例化。同时后者懒汉式中私有的static也会在构造器之前初始化,但是是null,这样就在第一次调用这个类的时候对instance进行了初始化,但是之后就不会再初始化了,原因也是static,这样看起来对于懒汉和饿汉的初始化和构造过程是一样的。
不过说道具体的地方饿汉式是在加载类的时候创建对象,而懒汉式是在调用getInstance时候创建对象,那么这样他们在创建的时候还是用一定的区别的。
下面借着这个Singleton模式简单的做了一个Log工具,实现对于一个日志工具不会多次初始化,并且不会覆盖掉而是尾加,Log4j就是一个Singleton的实例
~~~
package com.designpattern.singleton;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Log {
private static final String DefalutLogFilePathName = System
.getProperty("user.dir")
+ File.separator + "user.log";
private static Log log;
private static PrintWriter pw;
private Log() {
pwinit();
}
public static synchronized void log(String message) {
if (log == null || pw == null) {
log = new Log();
}
if (pw != null) {
pw.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
.format(new Date())
+ " : " + message);
}
}
public static void destroy() {
log = null;
if (pw != null) {
pw.close();
}
}
private void pwinit() {
if (pw == null) {
try {
pw = new PrintWriter(new FileWriter(DefalutLogFilePathName,
true), true);
} catch (IOException e) {
e.printStackTrace();
pw = null;
}
}
}
}
~~~
在Client端对Log进行操作,不会覆盖结果,测试成功
~~~
package com.designpattern.singleton;
public class Client {
public static void main(String[] args) {
System.out.print("Log");
System.out.print("\t");
Log.log("start");
Log.log("middle");
Log.log("end");
Log.destroy();
}
}
~~~
输出结果:
~~~
2012-04-05 14:46:15 : start
2012-04-05 14:46:15 : middle
2012-04-05 14:46:15 : end
~~~
在单例模式中,客户调用类的实例时,只能调用一个公共接口,这就是为整个开发团队提供了共享的概念。
但是但是模式的类在实例化以后,不能被别的类继承,在分布式系统中,当系统中的单例类被复制运行在多个虚拟机下时,在每一个虚拟机下都会创建一个实例对象,此时如果想知道具体那个虚拟机下运行着哪个单例对象是很困难的,而且单例类很难实现序列化。