单例模式就是存在一个实例。这个实例在整个应用中只存在一个。接下来我就演示下几种常见的单例:
1 饿汉模式
饿汉模式就是类一加载,该实例就创建了,
public class SingleSimple {
/**
* private代表是不向外部暴露出去,
*/
private static SingleSimple single=new SingleSimple();
private SingleSimple(){}//private代表不能new创建该实例。必须为private
public static SingleSimple newInstance(){
return single;
}
}
这就是一个简单的饿汉单例。
然后测试下:
public class SingleSimpleTest {
public static void main(String[] args) {
SingleSimple single01=SingleSimple.newInstance();
SingleSimple single02=SingleSimple.newInstance();
System.out.println(single01==single02);
}
}
运行结果:
可以看到,运行结果为true,这就说明single01和single02是指向内存中同一个。
2 懒汉模式(线程不安全)
懒汉模式是根据调用者是不是想要个实例,要的话就创建一个,不要的话就不创建。
public class SingleSimple {
/**
* private代表是不向外部暴露出去,
*/
private static SingleSimple single=null;
private SingleSimple(){}//private代表不能new创建该实例。
public static SingleSimple newInstance(){
if (single==null) {
single=new SingleSimple();
}
return single;
}
}
这是一个超级简单的懒汉单例。
测试:
public class SingleSimpleTest {
public static void main(String[] args) {
SingleSimple single01=SingleSimple.newInstance();
SingleSimple single02=SingleSimple.newInstance();
System.out.println(single01==single02);
}
}
运行结果:
还是为true说明我们写的懒汉单例没问题。
2 懒汉模式(线程安全)
懒汉模式是根据调用者是不是想要个实例,要的话就创建一个,不要的话就不创建。
public class SingleSimple {
/**
* private代表是不向外部暴露出去,
*/
private static SingleSimple single=null;
private SingleSimple(){}//private代表不能new创建该实例。
public static synchronized SingleSimple newInstance(){
if (single==null) {
single=new SingleSimple();
}
return single;
}
}
这是一个超级简单的懒汉单例。只是加了个同步
测试:
public class SingleSimpleTest {
public static void main(String[] args) {
SingleSimple single01=SingleSimple.newInstance();
SingleSimple single02=SingleSimple.newInstance();
System.out.println(single01==single02);
}
}
运行结果:
还是为true说明我们写的懒汉单例没问题。
4, 饿汉单例变形(静态代码块)。
静态代码块实现,静态代码块也是随着类加载的。只加载一次
public class SingleSimple {
/**
* private代表是不向外部暴露出去,
*/
private static SingleSimple single=null;
private SingleSimple(){}//private代表不能new创建该实例。
static{
single=new SingleSimple();
}
public static SingleSimple newInstance(){
return single;
}
}
测试代码:
public class SingleSimpleTest {
public static SingleSimple single01=null;
public static SingleSimple single02=null;
public static void main(String[] args) {
single01=SingleSimple.newInstance();
single02=SingleSimple.newInstance();
System.out.println(single01==single02);
System.out.println(single02);
System.out.println(single01);
}
}
也为true ,说明这也是一种单例。
5, 静态内部类
public class SingleSimple {
private SingleSimple(){}//private代表不能new创建该实例。
static class InnerClass{
private static SingleSimple single=new SingleSimple();
}
public static SingleSimple newInstance(){
return InnerClass.single;
}
}
测试代码一样。不贴出来了:
运行结果:
还是为true,说明也是单例。
6 , 双重判断。
public class SingleSimple {
/**
* private代表是不向外部暴露出去,
* volatile关键字是代表可见性
*/
private static volatile SingleSimple single=null;
private SingleSimple(){}//private代表不能new创建该实例。
public static SingleSimple newInstance(){
if (single==null) {
synchronized (SingleSimple.class) {
if (single==null) {
single=new SingleSimple();
}
}
}
return single;
}
}
在第二个判断时,加了锁,线程是安全的。比第三种实现方式更安全。
测试代码一样,不贴出来了。
运行结果:
为true就说明单例是成功的。
7 , 枚举实现
枚举是java1.5才出现的。它也可以实现单例
public enum SingleSimple {
SINGLE;
}
测试代码:
public class SingleSimpleTest {
public static void main(String[] args) {
SingleSimple S1=SingleSimple.SINGLE;
SingleSimple S2=SingleSimple.SINGLE;
System.out.println(S1==S2);
System.out.println(S1);
System.out.println(S2);
}
}
运行结果:
为true说明单例成功了。如果不明白枚举的可以去看看。枚举其实挺重要的。
下面我就简单演示下怎么用枚举吧 :
首先是用enum声明的,就这样
public enum SingleSimple {
SINGLE01,SINGLE02,SINGLE03;
public void methodInEnum(){
System.out.println("我是来自枚举中的方法。。。");
}
}
然后就演示下怎么用枚举。
public class SingleSimpleTest {
public static void main(String[] args) {
SingleSimple s1=SingleSimple.SINGLE01;
SingleSimple s2=SingleSimple.SINGLE02;
SingleSimple s3=SingleSimple.SINGLE03;
s1.methodInEnum();//调用枚举中的方法
int position01=s1.ordinal();//返回的是第几个位置
int position02=s2.ordinal();//返回的是第几个位置
int position03=s3.ordinal();//返回的是第几个位置
System.out.println("SINGLE01的位置"+position01);
System.out.println("SINGLE02的位置"+position02);
System.out.println("SINGLE03的位置"+position03);
System.out.println(s1.compareTo(s2));//比较的是位置的大小
SingleSimple all[]= s1.values();//返回的是SINGLE01,SINGLE02,SINGLE03
/**
* 全部打印出来
*/
for (int i = 0; i <all.length; i++) {
System.out.println(all[i].name());
}
}
}
运行结果:
其实枚举也没什么难度,只有几个方法。我差不多都演示了。
总结:
单例模式我写了七种,目的就是一个,就是在整个应用中只存在一个实例。双重判断现在是用的最多的单例。
最后补充一句:其实反射可以创建实例。即使是构造方法被private修饰,也可以创建一个新实例,但是这样就不能构成单例。反射不会的,应该要加强了。哈哈。。。