Android 设计模式-单例模式
2020-06-10 本文已影响0人
涛涛123759
饿汉式单例
public class Person {
private static final Person INSTANCE = new Person();
private Person(){
}
public static Person getInstance() {
return INSTANCE;
}
}
优点:线程安全。
缺点:类加载的时候就进行初始化,对于初始化逻辑复杂的类,会导致类加载变慢。
** 懒汉式单例**
public class Person {
private static volatile Person sInstance = new Person();
private Person(){
}
public static Person getInstance() {
if (sInstance == null){
synchronized (Person.class) {
if (sInstance == null) {
sInstance = new Person();
}
}
}
return sInstance;
}
}
优点:volatile 声明单例引用,可以防止并发时初始化成员变量和对象实例化顺序可能会被打乱,双重校验锁定解决了多余的同步问题。
缺点:并发时存在极小的概率导致 DCL 失效,据说是百万分之一。
静态内部类单例 (推荐)
public class Person {
private Person(){
}
private static class Singleton {
private static final Person INSTANCE = new Person();
}
public static Person getInstance() {
return Singleton.INSTANCE;
}
}
优点:充分结合了懒汉式单例与饿汉式的优点,同时有效避开了它们的缺点,充分保证线程安全。推荐
缺点:会有的!
枚举型单例
public enum PersonEnum {
INSTANCE;
private PersonEnum() {
}
public void fun() {
// do something
}
}
使用方法:
PersonEnum.INSTANCE.fun();
优点:简洁,线程安全。
缺点:Google 官方强烈建议尽量不要使用 enum ,它会增加额外的内存占用,同时增加可执行文件 .dex 的大小,也不适用继承场景。
———————————————————————————————————————————
如何防止单例模式被JAVA反射攻击
如果要抵御这种攻击,可以修改构造器,让它在被要求创建第二个实例的时候抛出异常。
package com.effective.singleton;
public class ElvisModified{
private static boolean flag = false;
private ElvisModified(){
synchronized(ElvisModified.class) {
if(flag == false){
flag = !flag;
} else {
throw new RuntimeException("单例模式被侵犯!");
}
}
}
private static class SingletonHolder{
private static final ElvisModified INSTANCE = new ElvisModified();
}
public static ElvisModified getInstance() {
return SingletonHolder.INSTANCE;
}
public void doSomethingElse(){
}
}
测试代码:
public class ElvisModifiedReflectAttack{
public static void main(String[] args) {
try {
Class<ElvisModified> classType = ElvisModified.class;
Constructor<ElvisModified> c = classType.getDeclaredConstructor(null);
c.setAccessible(true);
ElvisModified e1 = (ElvisModified)c.newInstance();
ElvisModified e2 = ElvisModified.getInstance();
System.out.println(e1==e2);
} catch (Exception e) {
e.printStackTrace();
}
}
}