单例模式的六种写法
Posted Android编程精选
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单例模式的六种写法相关的知识,希望对你有一定的参考价值。
1 public class EagerSingleton {
2 //饿汉单例模式
3 //在类加载时就完成了初始化,所以类加载较慢,但获取对象的速度快
4 private static EagerSingleton instance = new EagerSingleton(); //静态私有成员,已初始化
5
6 private EagerSingleton()
7 {
8 //私有构造函数
9 }
10
11 public static EagerSingleton getInstance() //静态,不用同步(类加载时已初始化,不会有多线程的问题)
12 {
13 return instance;
14 }
15
16}
1 //懒汉式单例模式
2 //比较懒,在类加载时,不创建实例,因此类加载速度快,但运行时获取对象的速度慢
3 private static LazySingleton intance = null; //静态私用成员,没有初始化
4
5 private LazySingleton()
6 {
7 //私有构造函数
8 }
9
10 public static synchronized LazySingleton getInstance() //静态,同步,公开访问点
11 {
12 if(intance == null)
13 {
14 intance = new LazySingleton();
15 }
16 return intance;
17 }
18}
1 public class SingletonKerriganD {
2
3 /**
4 * 单例对象实例
5 */
6 private volatile static SingletonKerriganD instance = null; //这里加volatitle是为了避免DCL失效
7
8 //DCL对instance进行了两次null判断
9 //第一层判断主要是为了避免不必要的同步
10 //第二层的判断则是为了在null的情况下创建实例。
11 public static SingletonKerriganD getInstance() {
12 if (instance == null) {
13 synchronized (SingletonKerriganD.class) {
14 if (instance == null) {
15 instance = new SingletonKerriganD();
16
17 }
18 }
19 return instance;
20 }
21}
什么是DCL失效问题?
给实例分配内存 调用构造函数,初始化成员字段 将instance 对象指向分配的内存空间(此时sInstance不是null)
1 public class Singleton {
2 private Singleton(){
3
4 }
5 private static class SingletonHolder{
6 private final static Singleton instance= new Singleton();
7 }
8 public static Singleton getInstance(){
9 return SingletonHolder.instance;
10 }
11}
这种方式如何保证单例且线程安全?
这种方式能否避免反射入侵?
1 package eft.reflex;
2
3 public class Singleton {
4
5 private int a;
6
7 private Singleton(){
8 a= 123;
9 }
10 private static class SingletonHolder{
11 private final static Singleton instance= new Singleton();
12 }
13 public static Singleton getInstance(){
14 return SingletonHolder.instance;
15 }
16
17 public int getTest(){
18 return a;
19 }
20}
1 public static void main(String[] args) throws Exception {
2 //通过反射获取内部类SingletonHolder的instance实例fInstance
3 Class cInner=Class.forName( "eft.reflex.Singleton$SingletonHolder");
4 Field fInstance=cInner.getDeclaredField( "instance");
5
6 //将此域的final修饰符去掉
7 Field modifiersField = Field. class.getDeclaredField( "modifiers");
8 modifiersField.setAccessible( true);
9 modifiersField.setInt(fInstance, fInstance.getModifiers() & ~Modifier.FINAL);
10
11 //打印单例的某个属性,接下来要通过反射去篡改这个值
12 System. out.println( "a="+ Singleton.getInstance().getTest());
13
14 //获取该单例的a属性fieldA
15 fInstance.setAccessible( true);
16 Field fieldA=Singleton. class.getDeclaredField( "a");
17
18 //通过反射类构造器创建新的实例newSingleton(这里因为无参构造函数是私有的,不能通过Class.newInstance创建实例)
19 Constructor constructor=Singleton. class.getDeclaredConstructor();
20 constructor.setAccessible( true);
21 Singleton newSingleton= (Singleton) constructor.newInstance();
22
23 //让fInstance指向新的实例newSingleton,此时我们的单例已经被偷梁换柱了!
24 fInstance. set( null,newSingleton);
25 //为盗版的单例的属性a设置新的值
26 fieldA.setAccessible( true);
27 fieldA. set(newSingleton, 888);
28
29 //测试是否成功入侵
30 System. out.println( "被反射入侵后:a="+ Singleton.getInstance().getTest());
31 fieldA. set(newSingleton, 777);
32 System. out.println( "被反射入侵后:a="+ Singleton.getInstance().getTest());
33}
1a=123
2被反射入侵后:a=888
3被反射入侵后:a=777
注意:上述四种方法要杜绝在被反序列化时重新声明对象,需要加入如下方法:
1 private Object readResolve() throws ObjectStreamException{
2 return sInstance;
3}
1class Resource{
2}
3
4 public enum SomeThing {
5 INSTANCE;
6 private Resource instance;
7 SomeThing() {
8 instance = new Resource();
9 }
10 public Resource getInstance() {
11 return instance;
12 }
13}
这种方式如何保证单例?
1...
2 public static final eft.reflex.SomeThing INSTANCE;
3 flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
4
5...
枚举类型为什么是线程安全的?
1 public class SingletonManager {
2
3 private static Map<String,Object> map= new HashMap<String, Object>();
4
5 private SingletonManager(){}
6
7 public static void registerService(String key,Object instance){
8 if (! map.containsKey(key)){
9 map.put(key,instance);
10 }
11 }
12
13 public static Object getService(String key){
14 return map.get(key);
15 }
16
17}
将构造函数私有化 通过静态方法获取一个唯一实例 保证线程安全 防止反序列化造成的新实例等。
只有一个对象,内存开支少、性能好(当一个对象的产生需要比较多的资源,如读取配置、产生其他依赖对象时,可以通过应用启动时直接产生一个单例对象,让其永驻内存的方式解决) 避免对资源的多重占用(一个写文件操作,只有一个实例存在内存中,避免对同一个资源文件同时写操作) 在系统设置全局访问点,优化和共享资源访问(如:设计一个单例类,负责所有数据表的映射处理)
一般没有接口,扩展难 android中,单例对象持有Context容易内存泄露,此时需要注意传给单例对象的Context最好是Application Context
1final class SystemServiceRegistry {
2 private static final HashMap<Class<?>, String> SYSTEM_SERVICE_NAMES = new HashMap<Class<?>, String>();
3 private static final HashMap<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS = new HashMap<String, ServiceFetcher<?>>();
4 private SystemServiceRegistry() { }
5
6 static {
7 registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,
8 new CachedServiceFetcher<LayoutInflater>() {
9 @Override
10 public LayoutInflater createService(ContextImpl ctx) {
11 return new PhoneLayoutInflater(ctx.getOuterContext());
12 }});
13 registerService(Context.ACTIVITY_SERVICE, ActivityManager.class,
14 new CachedServiceFetcher<ActivityManager>() {
15 @Override
16 public ActivityManager createService(ContextImpl ctx) {
17 return new ActivityManager(ctx.getOuterContext(), ctx.mMainThread.getHandler());
18 }});
19 .......
20 }
21
22 public static Object getSystemService(ContextImpl ctx, String name) {
23 ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
24 return fetcher != null ? fetcher.getService(ctx) : null;
25 }
26 private static <T> void registerService(String serviceName, Class<T> serviceClass, ServiceFetcher<T> serviceFetcher) {
27 SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
28 SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
29 }
30 ......
31}
1 public static WindowManagerGlobal getInstance() {
2 synchronized (WindowManagerGlobal.class) {
3 if (sDefaultWindowManager == null) {
4 sDefaultWindowManager = new WindowManagerGlobal();
5 }
6 return sDefaultWindowManager;
7 }
8}
推荐↓↓↓
长
按
关
注
以上是关于单例模式的六种写法的主要内容,如果未能解决你的问题,请参考以下文章