设计模式一共有23种,分为三大类
创建型模式:
单例模式,工厂模式,抽象工厂模式,原型模式
结构型模式:
适配器模式,桥接模式,装饰器模式,组合模式,外观模式,享元模式,代理模式
行为型模式:
模板方法模式,命令模式,迭代器模式,观察者模式,中介者模式,备忘录模式,解释器模式,策略模式,责任链模式,访问者模式
这23种设计模式本质上是对Java面向对象原则实际运用,这23种设计模式更多是为了提高代码的可读性、可复用性、可维护性、稳健性以及安全性。让我们写出的代码更加整洁。
一、创建型模式
单例模式:
最重要的作用:保证类只有一个实例,并且提供一个访问该实例的全局访问点。
常见的五种实现单例模式的方法
主要:
- 饿汉式(线程安全,调用效率高,但不能延迟加载,资源利用效率低)
- 懒汉式(线程安全,调用效率不高,可以延迟加载,可以真正用时再创建,方法实现了同步,调用效率低)
其他:
- 双重检测锁(线程安全,调用效率低,可以延迟加载,偶尔会出问题)
- 静态内部类(调用效率高,可以延迟加载)
- 枚举单例(调用效率高,不可以延迟加载,可以避免反射和反序列化重新创建多个实例)
1、饿汉式实现
public class HungryMan {
//类初始化时立即加载这个对象
private static HungryMan instance=new HungryMan();
//私有化的构造器
private HungryMan() {
}
//对外开放的获取唯一实例的方法
public static HungryMan getInstance() {
return instance;
}
}
class Demo06{
public static void main(String args[]) {
//无法通过new获得该对象的实例
//HungryMan instance=new HungryMan();
//只能通过对外提供的方法获取该对象的唯一实例
HungryMan instance1=HungryMan.getInstance();
HungryMan instance2=HungryMan.getInstance();
//多次获取的实例时,本质上是同个实例
System.out.println(instance1);
System.out.println(instance2);
}
}
csdn.HungryMan@7852e922
csdn.HungryMan@7852e922
类在加载时,线程时天然安全的,static变量在会在类加载时初始化。此时不会涉及多个多线程对象的访问的情况,故可以省去synchronized关键字。
缺点:如果只是加载该对象,而不是需要获取该对象的实例,会造成资源的浪费。
2、懒汉式实现
public class LazyMan {
private static LazyMan instance;
private LazyMan() {
}
//使用了同步,但是效率低了
public static synchronized LazyMan getInstance() {
if(instance==null) {
instance= new LazyMan();
return instance;
}
return instance;
}
}
class Demo07{
public static void main(String args[]) {
//无法通过new获得该对象的实例
//LazyMan instance=new LazyMan();
//只能通过对外提供的方法获取该对象的唯一实例
LazyMan instance1=LazyMan.getInstance();
LazyMan instance2=LazyMan.getInstance();
//多次获取的实例时,本质上是同个实例
System.out.println(instance1);
System.out.println(instance2);
}
}
csdn.LazyMan@7852e922
csdn.LazyMan@7852e922
缺点:与饿汉式相比,可以延迟的获取该对象的实例,但是获取实例时,都需要同步,使并发效率低了。
3、双重检查锁实现
public class DoubleCheck {
private static DoubleCheck instance=null;
private DoubleCheck() {
}
//方法同步 调用效率低
public static synchronized DoubleCheck getInstance() {
if(instance==null) {
DoubleCheck d;
synchronized(DoubleCheck.class) {
d=instance;
if(d==null) {
synchronized(DoubleCheck.class) {
if(d==null) {
d=new DoubleCheck();
}
}
instance=d;
}
}
}
return instance;
}
}
class Demo08{
public static void main(String args[]) {
//无法通过new获得该对象的实例
//DoubleCheck instance=new DoubleCheck();
//只能通过对外提供的方法获取该对象的唯一实例
DoubleCheck instance1=DoubleCheck.getInstance();
DoubleCheck instance2=DoubleCheck.getInstance();
//多次获取的实例时,本质上是同个实例
System.out.println(instance1);
System.out.println(instance2);
}
}
csdn.DoubleCheck@7852e922
csdn.DoubleCheck@7852e922
缺点:使用了同步方法,效率变低了,由于编译器优化和JVM底层内部模型原因,有时会出错,不建议使用。
4、静态内部类
public class StaticInsideClass {
//静态内部类
private static class StaticInside{
//static final 保证了内存只有一个实例存在
private static final StaticInsideClass instance=new StaticInsideClass();
}
private StaticInsideClass() {
}
public static StaticInsideClass getInstance() {
return StaticInside.instance;
}
}
class Demo09{
public static void main(String args[]) {
//无法通过new获得该对象的实例
//StaticInsideClass instance=new StaticInsideClass();
//只能通过对外提供的方法获取该对象的唯一实例
StaticInsideClass instance1=StaticInsideClass.getInstance();
StaticInsideClass instance2=StaticInsideClass.getInstance();
//多次获取的实例时,本质上是同个实例
System.out.println(instance1);
System.out.println(instance2);
}
}
csdn.StaticInsideClass@7852e922
csdn.StaticInsideClass@7852e922
外部类没有static属性,不会立即加载对象,同时兼备效率高和延迟加载的优势
5、枚举单例
public enum EnumInstance {
// 没有延迟加载
// 避免反射和反序列化这个漏洞
// 这个枚举元素本身就是单例对象
INSTANCE;
// 添加自己需要的操作
public void Operation() {
}
}
class Demo10{
public static void main(String args[]) {
EnumInstance instance1=EnumInstance.INSTANCE;
EnumInstance instance2=EnumInstance.INSTANCE;
System.out.println(instance1);
System.out.println(instance2);
}
}
INSTANCE
INSTANCE
枚举本身就是单例模式,实现简单,可以避免反射和反序列化,但是不能够延迟加载
有兴趣的自行测一下这五种方法生成实例的效率。这里就不做测试。
在需要实现单例模式时,如果单例对象占用资源少,不需要延迟加载,则使用枚举式大于饿汉式
若单例对象占用资源多,需要延迟加载,则使用静态内部类大于懒汉式
以上就是对关于23种设计模式中的单例模式进行了了解。