深度认识单例模式;在Android源码中的应用(1)

单例模式的类图看起来很简单,一个私有的当前类型的成员变量,一个私有的构造方法,一个 getInstance 方法,**创建对象不再通过new 而通过 getInstance 让该类自行创建。**相信我们大多数人使用的单例模式都是这种,因为太简单了。但是单例模式的写法可不止这一种。接下来我们一起来看一下单例模式的九种写法。

单例模式的九种写法

一、饿汉式(静态常量)

/**

  • 饿汉式(静态常量)
    */
    class King {
    private static final King kingInstance = new King();

static King getInstance() {
return kingInstance;
}

private King() {
}
}

  • 优点:这种写法比较简单,就是在类装载的时候就完成实例化。避免了线程同步问题。

  • 缺点:在类装载的时候就完成实例化,没有达到Lazy Loading的效果。如果从始至终从未使用过这个实例,则会造成内存的浪费。

二、饿汉式(静态代码块)

/**

  • 饿汉式(静态代码块)
    */
    class King {
    private static King kingInstance;

static {
kingInstance = new King();
}

private King() {
}

public static King getKingInstance() {
return kingInstance;
}
}

  • 优点:这种写法比较简单,就是在类装载的时候就完成实例化。避免了线程同步问题。

  • 缺点:在类装载的时候就完成实例化,没有达到Lazy Loading的效果。如果从始至终从未使用过这个实例,则会造成内存的浪费。

三、懒汉式(线程不安全)

/**

  • 懒汉式(线程不安全)
    */
    public class King {
    private static King kingInstance;

private King() {
}

public static King getKingInstance() {
if (kingInstance == null) {
kingInstance = new King();
}
return kingInstance;
}
}

优点:懒加载,只有使用的时候才会加载。

缺点:但是只能在单线程下使用。如果在多线程下,一个线程进入了if (singleton == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。所以在多线程环境下不可使用这种方式。

四、懒汉式(线程安全)

/**

  • 懒汉式(线程安全,同步方法)
    */
    public class King {
    private static King kingInstance;

private King() {
}

public static synchronized King getKingInstance() {
if (kingInstance == null) {
kingInstance = new King();
}
return kingInstance;
}
}

  • 优点:懒加载,只有使用的时候才会加载,获取单例方法加了同步锁,保障线程安全。

  • 缺点:效率太低了,每个线程在想获得类的实例时候,执行getInstance()方法都要进行同步。

五、懒汉式(线程安全,同步代码块)

/**

  • 懒汉式(线程安全,同步代码块)
    */
    public class King {
    private static King kingInstance;

private King() {
}

public static King getKingInstance() {
if (kingInstance == null) {
synchronized (King.class) {
kingInstance = new King();
}
}
return kingInstance;
}
}

  • 优点:改进了第四种效率低的问题。

  • 缺点:不能完全保证单例,假如一个线程进入了if (singleton == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。

六、双重检查(DCL)

/**

  • 双重检查(DCL)
    */
    public class King {

private static volatile King kingInstance;

private King() {
}

public static King getKingInstance() {
if (kingInstance == null) {
synchronized (King.class) {
if (kingInstance == null){
kingInstance = new King();
}
}
}
return kingInstance;
}
}

  • 优点:线程安全;延迟加载;效率较高。

  • 缺点:JDK < 1.5 的时候不可用

  • 不可用原因:由于volatile关键字会屏蔽Java虚拟机所做的一些代码优化,可能会导致系统运行效率降低,而JDK 1.5 以及之后的版本都修复了这个问题。(面试装逼用,谨记!!!)

七、静态内部类

/**

  • 静态内部类
    */
    public class King {

private King() {
}

private static class KingInstance{
private static final King KINGINSTANCE = new King();
}

public static King getInstance(){
return KingInstance.KINGINSTANCE;
}
}

  • 优点:避免了线程不安全,延迟加载,效率高。

  • 缺点:暂无,最推荐使用。

  • 特点:这种方式跟饿汉式方式采用的机制类似,但又有不同。

  • 两者都是采用了类装载的机制来保证初始化实例时只有一个线程。不同的地方在饿汉式方式是只要Singleton类被装载就会实例化,没有Lazy-Loading的作用,而静态内部类方式在Singleton类被装载时并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载SingletonInstance类,从而完成Singleton的实例化。 类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。

八、枚举

/**

  • 枚举
    */
    public enum King {
    KINGINSTANCE;
    }

  • 优点:不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。

  • 缺点:JDK 1.5之后才能使用。

九、容器类管理

/**

  • 使用容器实现单例模式(可以用于管理单例,有兴趣的可以尝试一下)
  • */
    class InstanceManager {
    private static Map<String, Object> objectMap = new HashMap<>();
    private InstanceManager(){}
    public static void registerService(String key,Object instance){
    if (!objectMap.containsKey(key)){
    objectMap.put(key,instance);
    }
    }
    public static Object getService(String key){
    return objectMap.get(key);
    }
    }

/**

  • 使用方式
  • Dog类就不贴出来了
  • 自己随便写个就行
  • 可以运行一下看看 打印的地址是否一致
    */
    class Test {
    public static void main(String[] args) {

InstanceManager .registerService(“dog”, new Dog());

Dog dog = (Dog) InstanceManager .getService(“dog”);
Dog dog2 = (Dog) InstanceManager .getService(“dog”);
Dog dog3 = (Dog) InstanceManager .getService(“dog”);
Dog dog4 = (Dog) InstanceManager .getService(“dog”);

System.out.println(dog);
System.out.println(dog2);
System.out.println(dog3);
System.out.println(dog4);
}
}

  • 优点:在程序的初始,将多种单例类型注入到一个统一的管理类中,在使用时根据key获取对象对应类型的对象。这种方式使得我们可以管理多种类型的单例,并且在使用时可以通过统一的接口进行获取操作, 降低了用户的使用成本,也对用户隐藏了具体实现,降低了耦合度。

  • 缺点:不常用,有些麻烦

九种写法的优劣对比;

①:不可用原因:由于volatile关键字会屏蔽Java虚拟机所做的一些代码优化,可能会导致系统运行效率降低,而JDK 1.5 以及之后的版本都修复了这个问题。(面试装逼用,谨记!!!)

②:这种方式跟饿汉式方式采用的机制类似,但又有不同。两者都是采用了类装载的机制来保证初始化实例时只有一个线程。不同的地方在饿汉式方式是只要Singleton类被装载就会实例化,没有Lazy-Loading的作用, 而静态内部类方式在Singleton类被装载时并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载SingletonInstance类,从而完成Singleton的实例化。 类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。

单例模式在Android源码中的应用

在我们每天接触的Android源码中其实也有很多地方用到了单例模式:

1、EventBus中获取实例:

private static volatile EventBus defaultInstance;
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();

总结

【Android 详细知识点思维脑图(技能树)】

image

其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。

虽然 Android 没有前几年火热了,已经过去了会四大组件就能找到高薪职位的时代了。这只能说明 Android 中级以下的岗位饱和了,现在高级工程师还是比较缺少的,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。

这里附上上述的面试题相关的几十套字节跳动,京东,小米,腾讯、头条、阿里、美团等公司19年的面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。

由于篇幅有限,这里以图片的形式给大家展示一小部分。

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!
PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。

由于篇幅有限,这里以图片的形式给大家展示一小部分。

[外链图片转存中…(img-y6dzUn35-1715338799718)]

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值