前言

“规划办法的重要性不用我多说,假定你想成为优异的 Android 工程师,规划办法是必需求把握的。” —— 《 Android 进阶之光 》 刘望舒

好了好了,我知道规划办法很重要了,我学还不可吗!

可是,我从哪个规划办法初步呢?那就单例吧,单例谁还能不会啊?嘿嘿!

然后,我翻开落了一层Android土的书:

《Android 进阶之光》《Android源码规划办法解析与实战》《HeadFirst规划办法》《鬼话规划办法》……(枪法不可androidstudio安装教程,枪贼多)

单例办法的完结办法有:饿汉办法、线程不安全的懒汉办法、线程安全的懒汉办法、DCL、静态内部类、枚举、容器

What the fuck!小小的单例办法竟java怎样读然有 7android平板电脑价格 种写法?!

还要牵扯“DCL失效问题”、“可序列化单例类数据库体系的特色的反序列化问题”?

没事,枪法不可,枪多来凑,弄他!

练枪,从知道枪初步

先看看定义:

单例办法用来保证一个类只需一个实例,自行实例化此实例,并供应一个访问此实例的全局访问点。

贴一下单例办法的 UML 类图,实际上就一个类数据库体系概论第五版课后答案

疯了吧!单例模式居然搞出7种写法!

打枪姿势

1、饿汉办法

先贴代码:

public class Singleton {
private static S安全期计算器ingleton instance = new Singleton();
// 私有结构,不允许外部经过结构实例化 Single数据库原理ton.class
private Singleto源码怎样做成app软件n() {
}
public static Singleton getInstance() {
return instance;
}
}

这种写法被称为“饿汉办法”。

这种办法,在类加载时,就完结了初java环境变量装备始化。

依据此,咱们安全能够总结出来,饿汉办法的单例办法有以下几个特征:

  • 1、依据类的加载机制,避免了多线程的同步问题android手机

  • 2、类加载时进行初始化,导致类加载速度变慢,但获取政策时速度很快;

  • 3、没有完结懒加载,假定从始至终未运用此实例,则会构成内存的浪费。

后续的各种单例办法的写法,都是依据饿数据库体系概论第五版课后答案汉办法展开而来,咱们了解了饿汉办法的上述特征的话,后续的各种写法的了解便是瓜熟蒂落了。

关于类加载的机遇,我之前的写法有误,现已做更正。

类是在第一次运用时被加载,而在Java虚拟机初始化运用程序时加载。

感谢“兰心之摇摆”,他特意到大众号里联系我纠正我的差错。

2、懒汉办法(线程不安全)

先贴代码:

publandroid下载安装ic class Singleton {android下载安装
private static Singleton instance;
// 私有结构,不允许外部经过结构实例化 Singleton.class
private源码 Singleton() {
}
public stat数据库体系工程师ic Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
retjava怎样读urn instance;
}
}

咱们能够很简单的发现,与饿汉办法比较,懒汉方安全手抄报法最大的改善是完结了懒加载

也便是说,此java环境变量装备类不用就不加载,用的话在第一次调用时会进行实例化。所安全期是哪几天以说,能够节省内存资数据库办理体系源。

其他,咱们还要留意的是,在多线程的状况下,此种写法有或许发生两种问题:

  • 1、创建了多个实例,就违背了单例办法的初衷;

  • 2、多线程的安全性问题,或许导致严重的后果,后文讲“DCL失效问题”数据库体系的特色时会一起聊。

众所周知,移动端的运用程序高并发的状况较少,所以这种写法的单例办法实际上是能够用的,只是确实没有后续的写法优异。

3、懒汉办法源码年代(线程安全)

已然上一种写法的懒汉办法最大的问题是线程不安全,那咱们就来处理下这个问题。

public class Singleton {
priva数据库原理te static Singleton instanceJava;
// 私有结构,不允许外部经过结构实例化 Sin安全手抄报g源码之家leton.class
private Singleton() {
}
public static synchron数据库体系ized Singleton getInst数据库原理ance() {
if (injavahdxxstance安全教育渠道 == null) {
instance = nandroid下载ew Single源码本钱ton();
}
return instanandroid/yunosce;
}
}

很简略,给 getInstance() 办法加了同步锁 synchronized

然而事实是,这种写法是最不主张的用法。由javascript于不论是不是多线程的环境,每次调用 getInstance() 办法都要进行同步,构成了不用要的同步开支。

4、两层查看办法(Djava根底知识点CL)

这种写法就很优异了,不过也存在一些问题,先看代码吧。

public class Singleton {
priv源码编辑器ate st安全出产法atic Singleton instance;
// 私有结构,不允许外部经过结构实例化 Singleton.class
private Sing安全出产法leton() {
}
public static Singleton getInstance() {
// 10行
if (instance == null) {
// 11行
synchronized (Singjava编译器leton.class) {
// 12行
if (instance == null) {
// 13行
instance = new Singleton();
}
}
}
return instance;
}
}

咱们看这段代码 12 ~ 14 行,自然会为了完结懒加载;

而代码 11 行,则是为了处理懒加载的线程不安全的问题;

亮点在代码 10 行,在进行同步前,先判空,假定类已经实例化,则不再进行同步java环境变量装备,处理了每数据库体系次调用 getInstance() 办法都要同步源码年代构成的不用要的同步开支java面试题问题。

综上,DCL 的写法一会儿处理了上述 3 种单源码怎样做成app软件例办法写法安全期是哪几天的悉数问题。

那 DCL 的写法存在的问题是什么呢?

DCL 失效问题(划要点)!

看代码 13 行 instance = new Singleton() ,咱们都知道,这是一条java环境变量装备原子操作,拆分来看的话,大致分为三个进程:

  • 1、给 Singjava怎样读leton 的实例分配内存空间;

  • 2、调用 Singleton 的结构函数,初始化其成员变量;

  • 3、将 instance 政策指向分配的内存空间(此刻,instance 就不是 null 了)。

由于 Java 编译器允许处理安全期计算器器乱序实施(原因一),以及Android在 JDK 1.4 及 1.4 之前,JMM(Java Memory Model) 中 Cache、寄存器到主内存回写次序的规矩(原因二),导致上述 2、3 进程次序无法保证,也便是说实施的次序或许是 1-2-3,或许是 1-3-2。

1-2-3 的实施次序没什么问题,源码编辑器咱们java根底知识点来注重下 1-3-2 的实施次序。

假定线程 A 正在对 Singleton 进行实例化,实施了进程 1 和 3,还没有实施 2。这时分线程 B 进来了,判别 instance 不是 nul源码下载l,就取走了 insta源码下载nce 进行运用,可是 Singleton 的结构函数都还没实施,成员变安全手抄报量也没数据库规划有初始化,成员变量的指向实际上仍是 null 的,就出错了android下载

这便是 DCL 的失效问题,也是前面提及的 “线程不安全的懒汉办法” 中或许会出现的严重后果。

那怎样处理 DCL 的失效问题呢?

在 JDK 1.4 及 1.4 从前,无解。源码下载好在现在根本也不同这么低版其他 JDK 了。

在 JDK 1.5 及 1.5 往后,我java名优馆在线六区们能够在上述代码第 3 行 声明 instance 特色时参与关键字 volatile ,就能够保证 instance 政策源码编辑器每次都是从主内存中读取到的。

  private static volatile Singletojava环境变量装备n instance;

咱们来简略解释一下:

在 JMM 中,有主内存与作业内存的区别,一般声明的变量会在android平板电脑价格主内存中存在,并在作业内存中存在其一个拷贝。当作业内存对变量的拷贝进行修改时,会从作业内存同步到主内存。

安全出产法是当对变量进行非原子性操java模拟器作时,变量从作业内存到主内存的同步也是非原子性操作,在多线程的状况下,假定存与取一起数据库规划发生,就会出现线程不安全源码编辑器编程猫下载的问题。

volatile 关键字,具备 “有序性” 和 “可见性” 的特性。

  • 有序性

    阻止指令重排序,处理导数据库规划致 DCL 失效的原因一;

  • 可见性

    当某一作业内存对java根底知识点变量的拷贝进行修改时,会当即同步到主内存,一起将悉数作业内存中的变量的拷贝置为无效状况,则其他作业内存要取用此变量时就要从主内存中同步过来,处理导致 DCL 失效的原因二。

这样, volatile 关键字在 J安全DK >java游戏= 1.5 时能够处理 DCL数据库规划 失效的问题。

咱们知道,运用同步锁 synchronized 进行同步操作是比较消耗资源的,volatile 虽然要比 synchronized 节俭一些,不过仍是对性能有所损耗,那有没有更优异的单例办法写法呢?

有的,往下看吧!

5、运用android平板电脑价格静态内部类完结单例

这种写法能够说优异了,先看下代码吧。

publiandroidstudio安装教程c class Singleton{
private Single数据库体系的特色ton(){
}
public static Singleton getAndroidInstance(){
// 6行
return Siandroid是什么手机牌子ngletonHelper.instanjavahdxxce;
}
// 9行
privandroid体系ate static class SingletonHelper{
pr数据库原理ivate final static Singleton instance = new Singleton();
}
}

要搞懂这种方安全法,首先要搞懂 “饿汉办法”,咱们一起来了解下吧。

看这段代码 9 ~ 11 行,静态内部类 Singletojava名优馆在线六区nHelper 中的静态特色 instance 是跟android/yunos着 SingtonHelper 的加载而加载的,也便是说 一旦静态内部类 SingletonHelper 加载,就会对 Singleton 类进行实例化

那问题来了,静态内部类 Sijava编译器ngletonHelper 什么时分加载呢?

静态内部类不同于静态特色,不会跟着宿主类(Singletojavascriptn)的加载而加载源码本钱,是在第一次调用静态内部类的时分再由 Java 虚拟机进行加载(代码第 6 行),这样就避免了多线程的同步问题。

一起,这种写法也完结了懒加载,假定不调用 getIjavascriptnstance() 办法,就不会调用静态内部类对 Singleto数据库体系工程师n 进行实例化。

可序列化的单例类的反序列化问题

以上,咱们一起剖析了 5 种单例办法的写法,实际上他们都存在一个相同数据库规划的问题:可序列化的单例类的反序列化问题。

这样,咱们经过序列化能够将单例类的实例政策写到磁盘,然后再读回来,即进行反序列化,就能够得到此单例类的一个实例。即便结构函数是私有的,也是有办法获得单例类的一个新的实例的。

那怎数据库体系的特色样处理此问题呢?两种计划:

计划 1、运用类中可用的一个私有的钩子函数 readResolve() ,用来控制类的反序列化。看下代码示例吧。

import java.io.ObjectStreamException;
importandroid体系 java.io.Serializable;
public cla安全教育渠道登录ss Singleton implements Serializabl源码年代e{
private static final long serialVersionUID = 0L;
private static volatile Singleton instance;
// 私有结构,不允许外部经过结构实例化 Singleton.class
private Singleton() {
}
public static Singleton getInstance() {
if安全期 (instance == null) {
synchroniz数据库体系ed (Singleton.class) {
if (instan源码站ce == null) {
instanc源码编辑器编程猫下载e = new Singleton();
}
}
}
return instance;
}
// 钩子函数,用来控制类的反序列化操作
// 26行
private Object readResolve() throws ObjectStr源码年代eamException {
return instance;
}
}

看这段代码 26 ~ 28 行,经过此钩子函数,当对类进行反序列化时,回来咱们的单例的实例 ins源码超市tance ,就避免了反序列化时创建新实例的问题。

计划 2:便是下面咱们要说的第 6 种完结单例办法的android下载写法:枚举办法。

6、运数据库体系的特色用枚举完结单例

咱们知道,枚举在 Java 中与一般的类是相同的,能够有自己的特色,还能够有自己的办法,最重要的是,枚举实例的创建是线程安源码本钱全的,而且在任何状况下它都是一个单例(包括反序列化)

让咱们来看下代码示例吧。

public enum Singletonandroid体系 {
INSTANCE;
public void someMethod(){
}
}

哈哈哈,有没有被惊掉下巴?

没错,枚举完结单例的代码便是这么简略!

那怎样获得枚举完结的单例类的实例呢?也是十分简略。

Singleton instance = Singleton.INSTANCE;

秀啊数据库体系的特色!鹤立鸡安全教育渠道登录进口群!

7、运用容器完结安全教育渠道登录进口单例

这算是一种比较特android平板电脑价格其他写法了。仍是先来看代码示例,至于这种写法的好坏,请各位自己评判吧。

import java.util.HashMapandroid手机;
import javajava名优馆在线六区.util.Map;
publjava名优馆在线六区ic class SingletonManager {
// 存储单例类的容器
private static Map<String, Object> mSingletonM源码分享网ap = new HashMap<>();
// 不需求对此 单例办理类 进行实例化
private SingletonManager() {
}
// 向容器中注册单例类
public static void registerSingleton(String key, Object instance) {
if (!mSingletonMap.containsKey(key)) {
mSingletonMap.put(key, instance);
}
}
// 从容器源码站中获取单例类
public static Object getSingleton(String key) {android什么意思
return m数据库原理SingletonMap.get(key);
}
}

在程序初始化时,会将多种单例类型的政策注入到容器中,当需求运用时经过 key 从容器中取出相应的单例类型的政策。

这种写法,很明显能够很好对单例类型进行统一办理,存取操作对用户是透明且低耦合的,问题是容器中初始的单例类型会消耗资源,不论是否会被用到。

在 Android数据库体系的特色 体系中,体系等级的服务用的便是这种计划,如 AMS、WMS、LayoutInflater 等服务,会在适宜的时分以单例的办法注册到体系中,当需求调用相应服务的时分数据库规划,会经过 Context.getSystemService(String name) 获取。

总结

至此,本文要介绍的 7 种单例办法的写法就介绍完了。androidstudio安装教程

不论是哪种完结办法,中心原理都是首先将结构函数私有化,然后经过静态方android手机法得到相应类的实例。

至于选用哪一种,咱们要依据相应单例类的运用场景抉择,比如是否是高并发环境、JDK 版别是否 >= 1.5、单例类的实例对资源的消耗状况、对单例类实例的取用频率等。

单例办法的运用场景

单例的办法的运用规划很广,详细的运java模拟器用场景有以下几个:

  • 整个项目需求一个同享访问点或许需求同享数据;

  • 创建相应政策需求消耗很多资源,如 I/O 操作、数据库连接等;

  • 东西类政策等。

参看

  • 《Android源码规划办法解析与实战》

  • 《Android进阶之光》

  • 《HeadFirst 规划办法》

  • 《鬼话规划办法》

本文首发于 大众号:牛角尖尖上起舞,ID:niujiaojianhi,欢迎注重。