一、单例模式
单例模式是一种设计模式,用于确保一个类只有一个实例,并提供全局访问点以访问该实例。这在需要控制某些资源(如数据库连接、线程池、缓存等)的访问时非常有用。
1.使用场景:
1.当一个类的实例需要被多个客户端共享访问时。
2.当系统只需要一个公共的访问点来管理全局配置或共享资源时。
3.当需要限制某个类只能有一个实例,以节省系统资源。
2.实现要素
1.私有化构造方法(Private Constructor): 通过将类的构造方法设为私有,可以防止其他类直接通过 new 关键字来实例化该类。
2.静态方法或者静态变量(Static Method or Variable): 单例类通常会提供一个静态方法来获取该类的唯一实例。此外,通常还会有一个静态变量来保存该类的实例。
3.延迟实例化(Lazy Initialization): 单例类在首次被使用时才会被实例化,而不是在类加载时就创建实例。这种方式称为懒汉式单例模式。
4.线程安全性(Thread Safety): 如果单例类在多线程环境中被使用,需要确保它是线程安全的。否则,可能会导致多个线程同时创建多个实例,违反了单例模式的初衷。
5.防止反射攻击(Prevent Reflection Attack): 可以通过在构造方法中添加逻辑,防止通过反射机制来绕过单例模式的限制,强制创建多个实例。
3.饿汉式
启动时随着类的加载会构造一个对象,如果不使用就会出现资源浪费,如果加载内容复杂也会出现加载缓慢,影响服务进程。
public class SingletonHungry {
private SingletonHungry(){
}
//随着类的加载而加载
public static SingletonHungry getSingletonHungry(){
return new SingletonHungry();
}
}
4.懒汉式
创建静态对象,静态方法判空,使用时创建对象。但是会出现多线程问题。
public class SingletonLazy {
//私有构造方法: 单例类的构造方法必须私有化,以防止外部类直接实例化该类。
//静态变量: 单例类通常会包含一个静态变量来保存该类的唯一实例。
//静态方法: 提供一个静态方法用于获取该类的实例,该方法负责判断实例是否已经被创建,如果没有则创建新实例并返回。
private SingletonLazy(){
System.out.println(Thread.currentThread().getName());
}
private static SingletonLazy singletonLazy;
private static SingletonLazy getSingletonLazy(){
if (singletonLazy == null) {
singletonLazy = new SingletonLazy();
}
return singletonLazy;
}
public static void main(String[] args) {
for (int i=0;i<10;i++){
new Thread(()->{
SingletonLazy.getSingletonLazy();
},"线程" + i).start();
}
}
}
解决线程问题
private static volatile SingletonLazy singletonLazy;
//线程问题解决方案:
//1.在静态方法上加synchronized关键字
//2.lock锁
//3.双重锁,下面就是双重锁
private static SingletonLazy getSingletonLazy(){
if (singletonLazy == null){
//只会有一个对象
synchronized (SingletonLazy.class) {
if (singletonLazy == null) {
singletonLazy = new SingletonLazy();//不是一个原子性操作
/**
* 1.分配内存
* 2.执行构造方法,初始化对象
* 3.把对象指向所在的空间
* 会出现指令重排 所以需要加volatile
*/
}
}
}
return singletonLazy;
}
5.静态内部类
public class SingletonStatic {
private SingletonStatic(){
}
public static SingletonStatic getSingletonStatic(){
return InnerClass.singletonStatic;
}
public static class InnerClass{
private static final SingletonStatic singletonStatic = new SingletonStatic();
}
}
6.反射破解单例模式
第一次破坏: 通过SingletonReflex singletonReflex3 = SingletonReflex.getSingletonReflex();获取对象之后反射再次获取对应对象
解决方案: 在构造器内加锁
public class SingletonReflex {
//第一次破坏:通过SingletonReflex singletonReflex3 = SingletonReflex.getSingletonReflex();获取对象之后反射
//获取无参构造器
//Constructor<SingletonReflex> declaredConstructor = SingletonReflex.class.getDeclaredConstructor(null);
//无视private
// declaredConstructor.setAccessible(true);
//SingletonReflex singletonReflex1 = declaredConstructor.newInstance();
//解决方案 在构造器加一个锁
// synchronized (SingletonReflex.class){
// if (singletonReflex != null){
// throw new RuntimeException("不要用反射破坏单例模式");
// }
// }
private SingletonReflex() {
synchronized (SingletonReflex.class){
if (singletonReflex != null){
throw new RuntimeException("不要用反射破坏单例模式");
}
}
}
public volatile static SingletonReflex singletonReflex;
public static SingletonReflex getSingletonReflex() {
if (singletonReflex == null) {
synchronized (SingletonReflex.class) {
if (singletonReflex == null) {
singletonReflex = new SingletonReflex();
}
}
}
return singletonReflex;
}
public static void main(String[] args) {
try {
SingletonReflex singletonReflex3 = SingletonReflex.getSingletonReflex();
//获取无参构造器
Constructor<SingletonReflex> declaredConstructor = SingletonReflex.class.getDeclaredConstructor(null);
//无视private
declaredConstructor.setAccessible(true);
SingletonReflex singletonReflex1 = declaredConstructor.newInstance();
SingletonReflex singletonReflex2 = declaredConstructor.newInstance();
System.out.println(singletonReflex1);
System.out.println(singletonReflex2);
System.out.println(singletonReflex3);
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException |
InvocationTargetException e) {
throw new RuntimeException(e);
}
}
}
第二次破坏: 对象都是通过反射获取。
解决方案: 红绿灯模式,定义一个默认值在锁内判断
public class SingletonReflex2 {
private static boolean mbb = false;
private SingletonReflex2() {
synchronized (SingletonReflex2.class){
if (!mbb){
mbb = true;
}else {
throw new RuntimeException("破坏异常");
}
}
}
public volatile static SingletonReflex2 singletonReflex;
public static SingletonReflex2 getSingletonReflex() {
if (singletonReflex == null) {
synchronized (SingletonReflex2.class) {
if (singletonReflex == null) {
singletonReflex = new SingletonReflex2();
}
}
}
return singletonReflex;
}
public static void main(String[] args) {
try {
//获取无参构造器
Constructor<SingletonReflex2> declaredConstructor = SingletonReflex2.class.getDeclaredConstructor(null);
//无视private
declaredConstructor.setAccessible(true);
SingletonReflex2 singletonReflex1 = declaredConstructor.newInstance();
SingletonReflex2 singletonReflex2 = declaredConstructor.newInstance();
System.out.println(singletonReflex1);
System.out.println(singletonReflex2);
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException |
InvocationTargetException e) {
throw new RuntimeException(e);
}
}
}
第三次破坏 内部的公有元素也是可以修改的
Field mbb1 = SingletonReflex2.class.getDeclaredField("mbb");
mbb1.setAccessible(true);
mbb1.set("mbb",false);
解决方法: 反射无法破坏枚举单例
public enum EnumSingleton {
INSTANCE;
private EnumSingleton(){
}
private EnumSingleton getInstance(){
return INSTANCE;
}
public static void main(String[] args) throws Exception{
EnumSingleton instance = EnumSingleton.INSTANCE;
Constructor<EnumSingleton> declaredConstructor = EnumSingleton.class.getDeclaredConstructor(String.class,int.class);
declaredConstructor.setAccessible(true);
EnumSingleton enumSingleton = declaredConstructor.newInstance();
System.out.println(instance);
System.out.println(enumSingleton);
}
}
获取枚举内的信息
public enum UserAgreementEnum {
//1
ONE("", 1),
//2
TOW("", 2),
//3
THREE("", 3),
FOUR("",4),
FIVE("",5)
;
private final int number;
private final String desc;
public String getDesc() {
return desc;
}
public int getNumber() {
return number;
}
UserAgreementEnum(String desc, int number) {
this.desc = desc;
this.number = number;
}
/**
* 通过value取枚举
* @param number
* @return
*/
public static UserAgreementEnum getEnum(int number){
for (UserAgreementEnum enums : UserAgreementEnum.values()) {
if (enums.getNumber() == (number)) {
return enums;
}
}
return null;
}
/**
* 通过number取描述
* @param number
* @return
*/
public static String getDescByValue(int number) {
for (UserAgreementEnum enums : UserAgreementEnum.values()) {
if (enums.getNumber() == number) {
return enums.getDesc();
}
}
return "";
}
}
上述不足之处,望海涵