什么是单例
所谓单例,指的就是单实例,有且仅有一个类实例。
应用场景
常应用于不能存在多个实例的场景中。例:计算器等。
单例的多种写法
单例存在多种写法:饿汉式、懒汉式、加锁、静态内部类、枚举等。
饿汉式
饿汉式,顾名思义就是在类加载时就是创建类的单例,占用资源,线程安全(反射可破)。
★ 适用场景:占用资源少的类
public class HungarySingleton{
private void HungarySingleton(){}
private static HungarySingleton hs = new HungarySingleton();
public static HungarySingleton getInstance(){
return hs;
}
}
懒汉式
懒汉式是延迟加载,在首次实际调用实例时进行加载,线程不安全。
★ 适用场景:单线程
public class LasySingleton{
private void LasySingleton(){}
private static LasySingleton ls ;
pulbic static getInstance(){
if(null == ls) ls = new LasySingleton();
return ls;
}
}
单锁式
单锁式,使用synchronized来同步实例,但这种加锁方式在每一次执行时都要进行同步和判断,影响执行速度。非绝对线程安全(反射、序列化可破)
★ 适用场景:
public class SyncSingleton{
private void SyncSingleton(){}
private static SyncSingleton ss;
public static synchronized SyncSingleton getInstance(){
if(null == ss) ss = new SyncSingleton();
return ss;
}
}
双重判断锁
双重锁可以解决单锁式,每次访问的加锁问题
★ 适用场景:
public class DoubleSyncSingleton{
private DoubleSyncSingleton(){};
private static DoubleSyncSingleton dss;
public static DoubleSyncSingleton getInstance(){
if(null == dss){
synchronized (DoubleSyncSingleton.class){
if(null == dss) dss = new DoubleSyncSingleton();
}
}
return dss;
}
}
静态内部类
非绝对线程安全(反射可破)。
JVM在加载类的静态成员只能加载一次,这样JVM层就保证了只会有一个实例对象
延迟加载:当JVM加载一个类时,其内部不会被同时加载,当且仅当静态成员被调用时发生调用。
★ 适用场景:
public class StaticClassSingleton{
private StaticClassSingleton(){}
private class Singleton{
private static StaticClassSingleton scs = new StaticClassSingleton();
}
public static StaticClassSingleton getInstance(){
return Singleton.scs;
}
}
枚举
线程绝对安全(反射不可破)、浪费空间
★ 适用场景:
public class EnumSingleton{
INSTANCE;//单例的实例对象
private String str;
public void config(String str){
str = str;
}
public String doSomething(){
return str;
}
}
反射验单例
以单锁单例为例
public class Reflex{
public void static main(String[] argc){
System.out.println("破解前: "+(SyncSingleton.getInstance() == SyncSingleton.getInstance() ? "是同一对象" : "不是同一对象"));
try{
// 加载单例
Class<SyncSingleton> syncSingletonClass = (Class<SyncSingleton>) Class.forName("com.example.SyncSingleton");
// 通过反射获取无参构造器
Constructor<SyncSingleton> constructor = syncSingletonClass.getDeclaredConstructor();
// 通过无参构造器来生成实例
SyncSingleton syncSingleton1 = (SyncSingleton) constructor.newInstance();
SyncSingleton syncSingleton2 = (SyncSingleton) constructor.newInstance();
System.out.println("破解后:"+ (syncSingleton1 == syncSingleton2 ? "是同一对象" : "不是同一对象"));
}catch(Exception e){
e.printStackTrace();
}
}
}
测试结果
破解前: 是同一对象!
破解后:不是同一个对象
网友评论