单例模式保证一个类仅有一个实例,并且提供一个访问他的全局访问点,当系统需要某个类只能有一个实例时,就可以采用单例模式。
为什么要有单例模式
比如说一个学校只能有一个正校长,一个班级只能有一个班主任,这是因为这些事情在生活中具有唯一性,有且只能有一个。
单例模式的实现方式
保证单例模式仅有一个实例的核心思想就是类的构造函数只能是私有的,不允许外部通过new的形式来实例化对象。
直接实例化单例模式
package com.madman.base.designMode.singleton;
public class Singleton {
// 无参构造函数私有化 外界不能new对象
private Singleton() {
}
// 直接产生单例实例 因此是线程安全的
private static final Singleton singleton = new Singleton();
// 通过公有的static方法返回对象实例
public static Singleton getInstance() {
return singleton;
}
public static void main(String[] args) {
// 获取单例
Singleton.getInstance();
}
}
延迟实例化单例模式
package com.madman.base.designMode.singleton;
import sun.security.jca.GetInstance;
public class Singleton2 {
private Singleton2() {
}
private static Singleton2 singleton = null;
// 这种形势大家一眼就能看出是线程不安全的,那怎么样才能线程安全呢。。
public static Singleton2 getInstance() {
if (singleton == null) {
singleton = new Singleton2();
}
return singleton;
}
// 线程安全方式1
// 这种是线程安全的 但是整个方法都被sysnchronized 毋庸置疑是存在效率问题的,
// 所有获取实例的操作都是“串行的”,那么我们先优化一点点
public static synchronized Singleton2 getInstance2() {
if (singleton == null) {
singleton = new Singleton2();
}
return singleton;
}
// 线程安全方式2
// 这种是线程安全的,比上面的效率稍微高一点点
public static Singleton2 getInstance3() {
if (singleton == null) {
synchronized (Singleton2.class) {
if (singleton == null) {
singleton = new Singleton2();
}
}
}
return singleton;
}
}
通过内部类获取单例
package com.madman.base.designMode.singleton;
public class Singleton3 {
// 构造函数私有话 不能new实例
private Singleton3() {
System.out.println("测试什么时候调用私有化的构造函数");
}
// 定义静态私有内部类 只有当前类能调用,且是线程安全的
//
private static class My {
private static final Singleton3 singleton = new Singleton3();
}
public static Singleton3 getInstance() {
return My.singleton;
}
public static void main(String[] args) {
Singleton3 s1 = Singleton3.getInstance();
System.out.println("第一....");
Singleton3 s2 = Singleton3.getInstance();
System.out.println("第二....");
//运行这段代码发现Singleton3的私有构造函数只被调用了一次,
//这里为什么会调用一次呢,不是应该JVM启动的时候就自动加载了吗,其实不是这样的
//java虚拟机加载应用程序字节码的时候,单例对象并不是立即记载的,而是等被调用的时候才会加载
//所以上述中的new Singleton3();并没有在JVM加载程序的时候就执行,而是等执行 Singleton3.getInstance();
//的时候才被加载,从而调用了私有的构造方法。
//换一种理解,内部类只有被使用的时候才会被加载JVM里面去
}
}
案例
日志类的案例
package com.madman.base.designMode.singleton;
import java.io.*;
import java.util.*;
public class FileLogger {
private String path = "c:/jbd/log.txt";
private FileOutputStream out;
private FileLogger() throws Exception {
System.out.println("This is new instance!");
}
public void write(String msg) {
try {
Calendar c = Calendar.getInstance();
int y = c.get(Calendar.YEAR);
int m = c.get(Calendar.MONTH);
int d = c.get(Calendar.DAY_OF_MONTH);
int hh = c.get(Calendar.HOUR);
int mm = c.get(Calendar.MINUTE);
int ss = c.get(Calendar.SECOND);
String strTime = String.valueOf(y) + String.valueOf(m) + String.valueOf(d) + String.valueOf(hh) + String.valueOf(mm) + String.valueOf(ss);
String strContent = "content:\r\n" + msg + "\r\n";
byte buf[] = strTime.getBytes("gbk");
out.write(buf);
buf = strContent.getBytes("gbk");
out.write(buf);
out.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
public void close() {
try {
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
private static class My {
static FileLogger log;
static {
try {
log = new FileLogger();
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static FileLogger getInstance() {
return My.log;
}
}
总结
推荐使用内部类的形式实例化单例模式,这样既能线程安全又高效。