设计模式(一)单例模式详解
愉快的开始自己的技术积累 ,设计模式中使用最普遍的单例模式开始;
设计模式中最为大家熟悉的必须是单例模式,项目中 必须 使用到的套路。首先陈述下我对 框架,模式的一些理解。
从学校出来,开始面试的时候张口框架,闭口模式,真的问道什么是框架,我只能死鸭子嘴硬的强调 MVC ,MVP 等等技术博客上看到名词。
有句话说的好,熟读百遍,其义自见。不管是 六大开发原则,还是二十三种设计模式,亦或是熟悉的开发流程MVC MVP 等,都是套路。是前人总结,切实可行的套路、可以让你沿着这一条条康庄大道追到小姐姐的技术、模式。你可以照本宣科,应用起来可能生硬,出格,但是只要追到小姐姐,你就成功了。所以编程和泡妹一样。知道你为什么还没有女朋友吗?是你的套路用错了地方。
我一直认为 六大开发原则 是总纲,相当于武林中的内功心法,而设计模式是外在表现,是功法。所以熟记原则至关重要。
1、优化代码的第一步 —— 单一职责原则
2、让程序更加稳定,灵活 —— 开闭原则
3、构建扩展性更好的系统 —— 里氏替换原则
4、让项目拥有变化的能力 —— 依赖倒置原则
5、系统有更高的灵活性 —— 接口隔离原则
6、更好的可扩展原则 —— 迪米特原则
言归正传:此文文章是总结主讲 单例模式
- 什么是单例模式;
- 意图;
- 简单实现;
- 单例模式的几种实现方式;
- 关于单利的类,实例化。
- 来来,面试了 你怕不怕。
1、什么是单例模式:百度百科中解释, 单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中,应用该模式的类一个类只有一个实例。即一个类只有一个对象实例
2、意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
3、简单实现: 创建一个类的对象,私有化构造 private 修饰,对外提供一个唯一获取对象的静态方法。
public class SingleObject {
//创建 SingleObject 的一个对象
private static SingleObject instance = new SingleObject();
//让构造函数为 private,这样该类就不会被实例化
private SingleObject(){}
//获取唯一可用的对象
public static SingleObject getInstance(){
return instance;
}
}
4、单例模式的写法五花八门,功能各不相同。
(一):懒汉式 线程不安全写法;
不主动进行初始化,只有第一次调用的时候,才被动初始化话,但是因为没有枷锁,在多线程中基本不能使用。
/*这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。因为没有加锁 synchronized,
*所以严格意义上它并不算单例模式
*/
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
(二):懒汉式,线程安全写法
懒汉式的优化版本,加一个 synchronized ,保证其在多线程中正常的工作,但 枷锁的同时,也影响了其效率。说实在的,Andorid中使用 多线程同时访问的场景微乎其微。
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
(三):恶汉式,基于classloader 加载机制。避免了多线程同步的问题,但是容易产生垃圾对象。
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
(四):双检锁/双重校验锁、安全且在多线程情况下能保持高性能。
//volatile 开发中基本很少使用这个关键字,大概意思是保证并发编程的时候类的
// 1、保证可见性
// 2、保证有序性
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
(五)、关于单例模式的书写,大概有三种。但是基本没有用过了。有想了解的朋友可以自行百度。个人觉得,后面的写法大概就是另辟蹊径。简单粗暴的陈述 我很吊。。。像我这种平凡玩家,还是不去和它们过不去了。
5、关于单利的类,实例化
我使用单例模式的地方很单一,保存用户常用数据,保存APP 常用数据。方便你在程序任何地方都可以拿到这些常用信息,便于修改,保存。并在此调用。
这种使用场景,不可避免的需要序列化,而Andorid 中对类进行序列化的方式有两种。Serializable 和 Parcelable
其中Serializable是Java自带的,而Parcelable是安卓专有的。
Serializable使用IO读写存储在硬盘上。序列化过程使用了反射技术,并且期间产生大量的临时对象。优点代码少。
Parcelable是直接在内存中读写,我们知道内存的读写速度肯定优于硬盘读写速度,所以Parcelable序列化方式性能上要优于Serializable方式很多。但是代码写起来相比Serializable方式麻烦一些。
Serializable的使用:
public class Person implements Serializable {
private static final long serialVersionUID = -3139325922167935911L;
// 首先Serializable 是一个javaSE标记接口,所产生的序列化值和所发生序列化的bean
// 有关,如果没有设置此值,在发生反序列化的时候,改Bean的成员变量发生变更,可能导致
// 反序列化失败。
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Parcelable 的使用
(一):首先实现 Parcelable 接口需要重写的几个方法。
public final class Rect implements Parcelable {
public int left;
public int top;
public int right;
public int bottom;
private static Rect rect = new Rect();
public static final Parcelable.Creator<Rect> CREATOR = new
Parcelable.Creator<Rect>() {
public Rect createFromParcel(Parcel in) {
//从序列化中的对象创建原始对象
return new Rect(in);
}
public Rect[] newArray(int size) {
//创建指定长度的原始对象数组
return new Rect[size];
}
};
private Rect() {
}
public static Rect getIntent(){
return rect;
}
private Rect(Parcel in) {
readFromParcel(in);
}
public void writeToParcel(Parcel out) {
//
out.writeInt(left);
out.writeInt(top);
out.writeInt(right);
out.writeInt(bottom);
}
public void readFromParcel(Parcel in) {
left = in.readInt();
top = in.readInt();
right = in.readInt();
bottom = in.readInt();
}
}
OK,是不是没有任何难度,告诉你。如果你只知道这些出去面试。肯定碰一鼻子灰。
来道附加题:请问我对一个类进行单利,不管是懒汉 饿汉,还是双校验,我进行了序列化,之后再进行反序列化。那么请问我拿到这个类,还是唯一的吗?
?????
我刚听到这个问题的时候一脸懵逼,说实在的开发中并且没有注意这些问题,不过不会我们可以猜,大胆猜测,我麻痹从硬盘中取出数据,对于整个层序来说肯定不是唯一的了啊。
那么,怎么保证唯一;
好吧:答案是 重写readReslove,如此可以保证。至于原因,关心的建议百度。
public class ReadResolveDemo implements Serializable {
private static final long serialVersionUID = 1L;
private static final ReadResolveDemo INSTANCE = new ReadResolveDemo();
private ReadResolveDemo() {
}
public static ReadResolveDemo getInstance() {
return INSTANCE;
}
private Object readResolve() {
return INSTANCE;
}
}
那么,我使用反射拿到这个类,不是也不能保证此类的单一。
怎么那么多那么,你有病啊,自己写的程序,自己给自己挖坑。不过吐槽归吐槽,毕竟人家反编译,或者压根是你以SDK方式提供别人引用的呢。所以。。
保证 反射拿不到类就可以了吧,怎么保证呢:上代码
public class ReadResolveDemo implements Serializable {
private static final long serialVersionUID = 1L;
private static final ReadResolveDemo INSTANCE = new ReadResolveDemo();
private static boolean flag = false;
synchronized (Elvis.class) {
System.out.println(" try to instance");
if (flag == false) {
System.out.println("first time instance");
flag = !flag;
} else {
throw new RuntimeException("单例模式被侵犯!");
}
}
}
private ReadResolveDemo() {
}
public static ReadResolveDemo getInstance() {
return INSTANCE;
}
private Object readResolve() {
return INSTANCE;
}
}
文章的最后让我们举个栗子,这是我和朋友开玩笑讲解,的想出来。
怎么理解上述一系列操作呢?
一群工科生班级里,突然有一个女神级妹子,经过 同学A 的各种殷勤加跪舔,终于让你拿下了 妹子,作为自己的女朋友。
都是自己女朋友了,我肯定不能让班级里一群牲口在去调戏了啊, 加上单例,果断的想要约我女朋友社交,必须通过我才行,不然你们联系不上。因为他的电话只有我知道。
可是放假了,妹子要回家了(序列化了),我的天啊,这下子脱离我的掌控了。肿么办,不行手机给开个呼叫转移。你打电话联系就到我电话上了(重写 ReadReslove )
可是妹子要毕业了,父母给安排了一系列的相亲,那哪成,肿么办。哎、我去破坏他们。嗯,先加个判断,再抛个异常。老兄你放弃吧,妹子是我的。你通过Android爸爸提供的反射,也不行。
下一篇正在整理,关于使用SP 存储用户数据的工具类。一直在用,感觉挺带感的。就上传github上,供大家参详。