Android 设计模式之单例模式

目录

1. 单例的定义

2. 使用单例模式的目的

3. 单例的几种创建方式

3.1 单线程中运行单例

3.2 多线程中的单例

3.2.1 synchronized作用

3.3 静态内部类

4. 测试

5. Android Studio 快速创建SingleTon类

6. 单例模式在项目中运用是好是坏


1. 单例的定义

保证此类只有一个自己创建的实例对象存在,并且提供全局访问该实例对象的方法。

单例的四大原则:

1. 构造私有。

2. 以静态方法或者枚举返回实例。

3. 确保实例只有一个,尤其是多线程环境。

4. 确保反序列换时不会重新构建对象

2. 使用单例模式的目的

当一个对象被很多地方调用的时候,比如我们的网络请求库, okhttpClient, Retrofit, 我们可以只实例化一次,不用频繁的进行创建和销毁,从而来节省系统资源。

3. 单例的几种创建方式

单例的创建方式有很多中,个人认为主要是看是在单线程中运行还是在多线程中运行。

3.1 单线程中运行单例

在单线程中可以很好的运行,但是在多线程中存在线程安全问题,也称为懒汉式单例。

public class SingleTonVersionOne {
    private static SingleTonVersionOne ourInstance;

    public static SingleTonVersionOne getInstance() {
        if (ourInstance == null) {
            ourInstance = new SingleTonVersionOne();
        }
        return ourInstance;
    }

    private SingleTonVersionOne() {
    }
}

3.2 多线程中的单例

双重检索,在多线程中保持高性能。

public class SingleTonVersionTwo {
    /**
     * 为了防止出现DCL失效问题,所以用 volatile 关键字来修饰 instance。
     */
    private volatile static SingleTonVersionTwo ourInstance;

    public static SingleTonVersionTwo getInstance() {
        if (ourInstance == null){
            synchronized (SingleTonVersionTwo.class) {
                if (ourInstance == null) {
                    ourInstance = new SingleTonVersionTwo();
                }
            }
        }
        return ourInstance;
    }

    private SingleTonVersionTwo() {
    }
}

3.2.1 synchronized作用

首先,synchronized是Java中的一个关键字,来解决多线程中的并发同步问题。保证同一时刻最多只有一个线程(被synchronized修饰的代码或方法)执行,其他线程必须等待当前线程执行完后才能执行,从而保证了线程的安全。具体可以看我的另一篇文章 Android程序员重头学Synchronized

 3.2.2 DCL失效

    上述代码中,可以发现,我们为 ourInstance 加上了 volatile 关键字修饰,说是防止出现 DCL 失效问题。如果你想进一步研究,请看我的另一篇文章 Android Volatile 关键字学习

 3.3 静态内部类

确保线程安全,又保证单例的唯一性。

public class SingleTon {
    private SingleTon() {
    }

    private static class SingleTonHelper {
        private static SingleTon INSTANCE = new SingleTon();
    }

    public static SingleTon getInstance() {
        return SingleTonHelper.INSTANCE;
    }
}

静态内部类的优点是:外部类加载时并不需要立即加载内部类,内部类不被加载则不去初始化 INSTANCE,故而不占内存。即当 SingleTon 第一次被加载时,并不需要去加载 SingleTonHelper,只有当 getInstance() 方法第一次被调用时,才会去初始化 INSTANCE,且在第一次调用 getInstance() 方法会导致虚拟机加载 SingleTonHelper 类,这种方法不仅能确保线程安全,也能保证单例的唯一性,同时也延迟了单例的实例化。

4. 测试

新建一个activity来测试我们的SingleTon类

public class SingleTonPatternTestActivity extends AppCompatActivity {
    private static final String TAG = "SingleTonPatternTestActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_single_ton_pattern_test);

        SingleTonVersionOne instanceOne = SingleTonVersionOne.getInstance();
        SingleTonVersionOne instanceTwo = SingleTonVersionOne.getInstance();

        Log.d(TAG, "instanceOne hash code: " + instanceOne.hashCode());
        Log.d(TAG, "instanceTwo hash code: " + instanceTwo.hashCode());

        instanceOne.setName("IM Kobe");
        instanceOne.setName("IM James");

        TextView instanceOneTv = findViewById(R.id.instance_one);
        instanceOneTv.setText(instanceOne.getName());
        TextView instanceTwoTv = findViewById(R.id.instance_two);
        instanceTwoTv.setText(instanceTwo.getName());
    }
}

log信息为:

D/SingleTonPatternTestActivity: instanceOne hash code: 186887382
D/SingleTonPatternTestActivity: instanceTwo hash code: 186887382

运行结果:
        从上方打印出来的Log信息中可以看出,instanceOne和instanceTwo的hashCode是一样的,所有两个对象是一样的,指向同一内存地址。所以,当我们先给instanceOne设置Name为“IM Kobe”,然后接着给instanceTwo设置Name为“IM James”,本质上就是为同一对象设置了两值,且第一个值将被第二个值所覆盖,所以instanceOne和instanceTwo都将显示为“IM James”。如下图所示:

 

5. Android Studio 快速创建SingleTon类

利用Android Studio来快速创建SingleTon模式的类?

点击OK,新建成功

public class JereSingleTonPatternTest {
    private static final JereSingleTonPatternTest ourInstance = new JereSingleTonPatternTest();

    public static JereSingleTonPatternTest getInstance() {
        return ourInstance;
    }

    private JereSingleTonPatternTest() {
    }
}

一旦 JereSingleTonPatternTest 类被加载时就会去实例化 JereSingleTonPatternTest 对象出来存在内存中,以空间换时间,不存在线程安全问题,称之为饿汉式。

6. 单例模式在项目中运用是好是坏

正方:当很多地方用到一个实例化对象时,我们只需实例化一次,不需要在每次调用时实例化对象,用完然后在销毁,这样能很好的节省系统内存,并且实现起来很简单。很好用。

反方:单例模式就是一个静态的全局变量,你可以在项目中的任意位置调用它,并且修改它,所以有的人就认为不该使用单例模式,当不用单例的时候就需要用依赖注入的方法来解决这个问题。

总结:就我个人而言,我目前的项目已经开发了五六年之久,我可以在项目中看到很多单例,项目的前辈们很喜欢写单例,导致后面加进来的程序员也都跟着很喜欢写单例,包括我自己,单例真的很好用,实现起来也很简单,但我目前还是觉得要少用,毕竟这是个全局变量。很多登入的用户信息都开发成了单例,因为项目中很多地方,可以说百分之八十的类都跟用户信息这个类相关,所以我们开发成了单例。但,万一哪天,需要将app开发成多用户登入时,由于之前你将用户信息写成了单例,你就要重构这用户信息类了,重构影响的范围达到百分之八十。。。

 

 

End !欢迎交流指教!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值