单例模式的几种实现方式

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);
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值