Java-第十八部分-设计模式-UML和单例模式

637 阅读6分钟

设计模式全文

UML

  • Unified modeling language,统一建模语言
  • 类图,描述类与类之间的关心,最核心
  • 六种类的关系
  1. 依赖,Dependence,A用到了B类,A依赖B类;类的成员属性、方法的返回类型、方法的参数、方法中使用到
  2. 泛化(继承),generalization,依赖关系的特例;A类继承B类
  3. 实现,Implentation,依赖关系的特例;A类实现B类
  4. 关联,Association,依赖关系的特例,具有导航性,具有双向关系或单向关系
  5. 聚合,Aggregation,关联关系的特例,表示整体和部分的关系,整体和部分是可以分开的,可以分开的就是聚合的关系。B作为A的成员变量或者方法参数,通过setter方法设置,B聚合于A。例子,电脑鼠标键盘
  6. 组合,Composition,关联关系的特例,不可分离,整体和部分的关系,整体和部分不可分开;A实例化的同时,B实例化,共生共死;如果A类中,定义了对B类的级联删除(删除一个对象,必须把另一个对象删除),那么就是组合关系,同生共死

类成员案例

@startuml
class Test{
 - name
 - age
 + Test()
 + setter()
 + getter()
}
note right of Test : -私有,+公共
@enduml

demo2.png

类关系案例

@startuml
interface Class
A ..> Class : 依赖
B -- Class : 关联
C --|> Class : 泛化、继承
D ..|> Class : 实现
E o.. Class : 聚合
F *.. Class : 组合
note top of B : 特例包括聚合、组合
note top of E : 通过setter方法传递
note top of F
    Class是F的一个属性,
    当F被实例化后,Class也被实例化
    耦合性比聚合强一些
end note
@enduml

demo.png

单例模式

  • 采取一定方法,保证某个类在整个软件系统中,只存在一个对象实例,只提供一个获取其对其实例方法(静态方法)
  • 八种写法,推荐使用饿汉式、双重检查、静态内部类、枚举

饿汉式(静态常量)

  • 构造器私有化(防止new)
  • 类的内部创建对象
  • 向外暴露一个静态的公共方法 getInstance
class Singleton {
    //私有构造方法
    private Singleton() {
    }
    private final static Singleton instance = new Singleton();
    //公有的返回对象方法
    public static  Singleton getInstance() {
        return instance;
    }
}
  • 优点,比较简单,在类装载时(初始化阶段)就完成了类的实例化,避免了线程同步问题
  • 缺点,在类装载时完成实例化,没有达到懒加载Lazy Loading的效果,如果从开始到结束都没有使用这个实例,会造成内存的浪费
  • 通过类加载避免了线程同步问题,但是有多种原因导致类装载,不能保证这个单例对象懒加载,可能造成内存浪费

饿汉式(静态代码块)

  • 类装载时进行,优缺点与上面一样
class Singleton {
    //私有构造方法
    private Singleton() {
    }
    //静态代码块中进行初始化
    private static Singleton instance;
    static  {
        instance = new Singleton();
    }
    //公有的返回对象方法
    public static  Singleton getInstance() {
        return instance;
    }
}

懒汉式(线程不安全)

  • 优点,起到懒加载的效果,但是只能在单线程下使用
  • 缺点,在多线程下,一个线程进入了if语句的判断,但是还没有往下执行;另一个线程也通过了这个判断,会产生多个实例
  • 在实际开发中,不要使用
class Singleton {
    //私有构造方法
    private Singleton() {
    }
    //静态代码块中进行初始化
    private static Singleton instance;
    //公有的返回对象方法,使用到该方法时才去创建
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

懒汉式(线程安全,同步方法)

  • 解决了线程不安全的问题
  • 效率太低了,每个线程都想获取类的实例的时候,都要进行同步,要进行等待
  • 实际开发中,不推荐使用
class Singleton {
    //私有构造方法
    private Singleton() {
    }
    //静态代码块中进行初始化
    private static Singleton instance;
    //公有的返回对象方法,使用到该方法时才去创建
    //加入同步处理方法
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

懒汉式(线程不安全,同步代码块)

  • 本意是对第四种方式进行改进,但是并不能起到线程同步的作用
  • 线程安全都无法保证,都会进入if语句,进行等待,并产生多个实例,不能使用
class Singleton {
    //私有构造方法
    private Singleton() {
    }
    //静态代码块中进行初始化
    private static Singleton instance;
    //公有的返回对象方法,使用到该方法时才去创建
    //加入同步处理方法
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                instance = new Singleton();
            }
        }
        return instance;
    }
}

双重检查

  • volatile 让修改内容立即更新到内存,一个线程更新完毕,其他线程立即同步;防止指令重排,创建一个对象需要三条指令,可以会发生重排,导致引用指向了分配的地址,但对象还未创建完毕,判断校验不准确;保证操作在主存中进行

newdupinvokespecial

  • double-check保证了线程安全,懒加载,效率较高
  • 推荐使用
class Singleton {
    //私有构造方法
    private Singleton() {
    }
    private static volatile Singleton instance;
    //公有的返回对象方法,使用到该方法时才去创建
    //加入同步处理方法,双重检查,解决线程同步和懒加载
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

静态内部类

  • 类装载时,不会主动装载静态内部类,直到静态内部类被使用
  • 静态内部类装载时,是线程安全的
  • 类的静态属性只会在第一次加载类的时候初始化,内部类里面的东西不会被初始化
  • 避免了线程不安全,利用静态内部类的特点实现延迟加载,效率高
  • 推荐使用
class Singleton {
    private Singleton() {
    }
    //静态内部类
    private static class SingletonInstance {
        private final static Singleton INSTANCE = new Singleton();
    }
    public static Singleton getInstance() {
        return SingletonInstance.INSTANCE;
    }
}

枚举

  • 避免多线程同步问题,防止反序列化重新创建新的对象,推荐使用
  • 反序列化,把字节序列恢复为Java对象的过程,存在不可信数据进行反序列化,让程序产生非预期的对象,对程序进行破坏
  • 反射,将类信息转换成属性,可以调用私有方法
enum Singleton {
    INSTANCE;
    public void say() {
        System.out.println("instace");
    }
}

源码

  • runtime,饿汉式创建 image.png

注意事项

  • 单例模式保证了内存中该类只有一个对象,节约了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式提高性能
  • 场景:创建对象耗时过多,消耗资源过多(重量级对象);经常用到的对象,工具类对象,频繁访问数据库或文件的对象(数据源、session工厂)