1.饿汉式
:在类加载的过程中创建单例对象。线程安全,不需要实现锁机制,效率高。不是按需创建的,可能导致加载类时间较长,浪费资源。
饿汉式:线程安全、反射不安全、反序列化不安全 (添加 readResolve 方法后安全)
饿汉式的实现代码如下:
package com.shi.test;
import java.io.Serializable;
//饿汉模式
public class Single1 implements Serializable{
//私有化当前类对象的实例
private static Single1 instance=new Single1();
//私有化构造方法
private Single1() {};
//对外提供的共有方法
public static Single1 getInstance() {
return instance;
}
//防止反序列化创建新对象
public Object readResolve() {
return instance;
}
}
测试代码 如下:
package com.shi.test;
import java.lang.reflect.Constructor;
import org.junit.Test;
public class Single1Test {
@Test
public void test1() throws Exception{
//1.普通调用
Single1 s1 = Single1.getInstance();
Single1 s2 = Single1.getInstance();
//输出为true
System.out.println(s1 == s2);
//2.利用反射调用
Class cl = Single1.class;
Constructor constructor = cl.getDeclaredConstructor();
constructor.setAccessible(true);
Single1 s3 = (Single1) constructor.newInstance();
//输出为false
System.out.println(s1 == s3);
//3.利用反序列化调用
SerializeUtil.serializeObject(s1);
Single1 s4 = (Single1) SerializeUtil.unserializeObject();
//输出为true
System.out.println(s1 == s4);
}
}
其中的SerializeUtil定义为:
package com.shi.test;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class SerializeUtil {
//用于序列化对象到文件中
public static boolean serializeObject(Object obj) {
try(ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(new File("a.txt")))){
out.writeObject(obj);
out.flush();
out.close();
return true;
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return false;
}
//反序列化文件到对象中
public static Object unserializeObject() {
Object obj = null;
try(ObjectInputStream in = new ObjectInputStream(new FileInputStream(new File("a.txt")))){
obj = in.readObject();
in.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return obj;
}
}
2.登记式(静态内部类式)
:对饿汉式的更改。单例对象不是在类加载的过程中创建,而是在调用 getInstance() 方法的时候创建的,实现了延迟加载的功能。
登记式:线程安全、反射安全(提供检查机制)、反序列化安全(提供 readResolver 方法).
登记式的实现代码为:
package com.shi.test;
import java.io.Serializable;
//登记式
public class Single2 implements Serializable{
private Single2() {
//当instance不为空是若调用构造方法抛出异常
if(SingleHolder.instance != null) {
throw new IllegalStateException();
}
}
//私有化静态内部类
private static class SingleHolder {
//私有化静态类成员
private static Single2 instance = new Single2();
}
public static Single2 getInstance() {
return SingleHolder.instance;
}
//防止反序列化造成的数据不一致
public Object readResolve() {
return SingleHolder.instance;
}
}
测试代码为:
package com.shi.test;
import java.lang.reflect.Constructor;
import org.junit.Test;
public class Single2Test {
@Test
public void test1() throws Exception{
//1.普通调用
Single2 s1 = Single2.getInstance();
Single2 s2 = Single2.getInstance();
//输出为true
System.out.println(s1 == s2);
//2.反射调用
Class cl = Single2.class;
Constructor constructor = cl.getDeclaredConstructor();
constructor.setAccessible(true);
//会抛出异常
// Single2 s3 = (Single2)constructor.newInstance();
//3.反序列化
SerializeUtil.serializeObject(s1);
Single2 s4 = (Single2) SerializeUtil.unserializeObject();
//输出为true
System.out.println(s1 == s4);
//4.多线程访问
for(int i=0;i<20;i++) {
new Thread(new Runnable() {
@Override
public void run() {
//输出为同一个对象
System.out.println(Single2.getInstance());
}
}).start();
}
//防止主程序走的太快
System.in.read();
}
}
3.枚举式
:使用枚举包装类对象,枚举默认支持反射和反序列化安全。
:当类可能有继承关系时不能使用枚举类创建单例模式。
枚举类:线程安全,反射安全,反序列化安全。
实现代码如下:
package com.shi.test;
//枚举式
public enum Single3 {
//相当于Single3的一个实例对象
INSTANCE{
//重写抽象方法
@Override
public void doSome() {
System.out.println("----doSomeing");
}
};
//创建抽象方法
protected abstract void doSome();
}
测试类代码如下:
package com.shi.test;
import java.lang.reflect.Constructor;
import org.junit.Test;
public class Single3Test {
@Test
public void test1() throws Exception {
//1.使用一般调用
Single3 s1 = Single3.INSTANCE;
Single3 s2 = Single3.INSTANCE;
//输出结果为true
System.out.println(s1 == s2);
//2.使用反射创建新对象
Class cl = Single3.class;
// Constructor constructor = cl.getDeclaredConstructor();
// constructor.setAccessible(true);
// Single3 s3 = (Single3)constructor.newInstance();
//结果为抛出异常,enum默认反射安全
// System.out.println(s1 == s3);
//3.使用反序列化创建对象
SerializeUtil.serializeObject(s1);
Single3 s4 = (Single3) SerializeUtil.unserializeObject();
//enum默认支持反序列化安全
System.out.println(s1 == s4);
}
}