1、单例模式
-
一个类只能有一个实例。使用场景:创建一个对象消耗太多的资源或者在一个系统中不适合创建多个对象实例的情况下,可以采用单例模式。
-
优点:
- 可以保证内存中只有一个实例,减少了内存开销
- 可以避免对资源的多重占用
- 单例模式设置全局访问点,可以优化和共享资源的访问
-
缺点:
- 单例模式一般没有接口,扩展困难,如果要扩展,必须要修改原来的代码
- 单例模式的功能代码一般写在一个类中,如果设计不合理,很容易违背单一职责原则
-
单例模式的实现:
1.懒汉式:
第一次调用方法时实例化—双重检测锁模式的 DCL懒汉式
public class LazySingleton
{
private static volatile LazySingleton instance=null; //保证 instance 在所有线程中同步
private LazySingleton(){} //private 避免类在外部被实例化
public static synchronized LazySingleton getInstance()
{
//getInstance 方法前加同步
if(instance==null)
{
instance=new LazySingleton();
}
return instance;
}
}
=============================================================================
//1、单例模式--双重检测锁模式 DCL懒汉式
public class LazyMan {
private LazyMan(){
System.out.println(Thread.currentThread().getName()+" ok");
}
private volatile static LazyMan lazyMan;
//双重检测锁模式 的 单例模式 DCL懒汉式----在LazyMan对象前加volatile,在公有方法前加synchronized
// 做两次检测,再加锁。由于在多线程并发下,会出现不安全的情况,因此加锁来保证线程安全
public static LazyMan getInstance() {
if (lazyMan == null) {
synchronized (LazyMan.class) {
if (lazyMan == null) {
lazyMan = new LazyMan();//不是一个原子性操作
/**
* Instance instance = new Instance(); 发生了什么?
* 1.分配内存空间
* 2.执行构造方法,初始化对象
* 3.把这个对象指向这个空间
* 可能会发生指令重排 , 因此需要在 LazyMan lazyMan 前加入 volatile 关键字来禁止指令重排
*/
}
}
}
return lazyMan;
}
================================================================
//2、静态内部类---懒汉式
public class Holder {
private Holder(){
}
public Holder getInstance(){
return InnerHolder.HOLDER;
}
public static class InnerHolder{
private static final Holder HOLDER = new Holder();
}
}============================================================
//单例不安全,有反射---用枚举类来实现
//什么是枚举?枚举本身也是一个类
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance(){
return INSTANCE;
}
}
class Test{
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//反射
EnumSingle instance1 = EnumSingle.INSTANCE;
Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
declaredConstructor.setAccessible(true);
//报错:NoSuchMethodException: com.tao.single.EnumSingle.<init>()
EnumSingle instance2 = declaredConstructor.newInstance();
System.out.println(instance1);
System.out.println(instance2);
}
}
2.饿汉式:
一旦加载就创建该类的实例
public class HungrySingleton
{
private static final HungrySingleton instance=new HungrySingleton();
private HungrySingleton(){}
public static HungrySingleton getInstance()
{
return instance;
}
}
3.枚举:
- 枚举:本身也是一个Class类
- 反射无法破坏枚举类的单例模式
- 反编译:首先在cmd利用Java反编译,javap -p 类名.class ,如果未达到预期,则利用jad专业的工具进行反编译,jad -sjava 类名.class
//==========反编译后得到的枚举类=============
// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3)
// Source File Name: EnumSingle.java
package com.tao.single;
public final class EnumSingle extends Enum
{
public static EnumSingle[] values()
{
return (EnumSingle[])$VALUES.clone();
}
public static EnumSingle valueOf(String name)
{
return (EnumSingle)Enum.valueOf(com/tao/single/EnumSingle, name);
}
private EnumSingle(String s, int i)
{
super(s, i);
}
public EnumSingle getInstance()
{
return INSTANCE;
}
public static final EnumSingle INSTANCE;
private static final EnumSingle $VALUES[];
static
{
INSTANCE = new EnumSingle("INSTANCE", 0);
$VALUES = (new EnumSingle[] {
INSTANCE
});
}
}