Java - 设计模式记录

Java——23种设计模式_java设计模式有几种_Special Careଲଇଉକ的博客-CSDN博客

1. 什么是设计模式
设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。
设计模式基于面向对象设计原则。对接口编程而不是对实现编程,优先使用对象组合而不是继承。
使用设计模式是为了可重用代码、让代码更容易被他人理解、提高代码的可靠性。
设计模式一般有如下几个基本要素:模式名称、问题、目的、解决方案、效果、实例代码和相关设计模式,其中的关键元素包括以下四个方面:模式名称 ,问题 ,解决方案 ,效果 。
2. 设计模式的分类
根据目的(模式是用来做什么的)可分为创建型,结构型和行为型三类:
创建型模式:主要用于创建对象。
结构型模式:主要用于处理类和对象的组合。
行为型模式:主要用于描述类或对象如何交互和怎样分配职责。
根据范围,即模式主要是处理类之间的关系还是处理对象之间的关系,可分为类模式和对象模式两种。类模式处理类和子类之间的关系,这些关系通过继承建立,在编译时刻就被确定下来,是一种静态关系;对象模式处理对象间的关系,这些关系在运行时变化,更具动态性。

设计模式:

Java设计模式之单例模式_Special Careଲଇଉକ的博客-CSDN博客

单例模式定义:

答案:
单例模式是指在内存中只会创建且仅创建一次对象的设计模式。在程序中多次使用同一个对象且作用相同时,为了防止频繁地创建对象使得内存飙升,单例模式可以让程序仅在内存中创建一个对象,让所有需要调用的地方都共享这一单例对象

单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。这些应用都或多或少具有资源管理器的功能。每台计算机可以有若干个打印机,但只能有一个 Printer Spooler,以避免两个打印作业同时输出到打印机中。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。 总之,选择单例模式就是为了避免不一致状态。

单例模式的特点:
答案:

单例类只能有一个实例。

单例类必须自己创建自己的唯一实例。

单例类必须给所有其他对象提供这一实例。

单例模式保证了全局对象的唯一性,比如系统启动读取配置文件就需要单例保证配置的一致性。

单例模式的四大原则:
答案:

构造私有;

以静态方法或者枚举返回实例

确保实例只有一个,尤其是多线程环境

确保反序列化时不会重新构建对象。

实现单例模式的方式:
答案:

1)饿汉式(立即加载);

2)懒汉式(延迟加载);

3)同步锁(解决线程安全问题):

4)双重检查锁(提高同步锁的效率);

5 静态内部类;

6)内部枚举类实现(防止反射攻击);

单例模式常见的应用场景:
答案:

WindowsTask Manager(任务管理器)就是很典型的单例模式

windowsRecycle Bin(回收站)也是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例。

项目中,读取配置文件的类,一般也只有一个对象。没有必要每次使用配置文件数据,每次new一个对象去读取。

网站的计数器,一般也是采用单例模式实现,否则难以同步。

应用程序的日志应用,一般都会用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作否则内容不好追加。

数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。

操作系统的文件系统,也是大的单例模式实现的具体例子,一个操作系统只能有一个文件系统。

Application 也是单例的典型应用(Servlet编程中会涉及到)

Spring中,每个Bean默认就是单例的,这样做的优点是Spring容器可以管理

servlet编程中,每个Servlet也是单例

spring MVC框架/struts1框架中,控制器对象也是单例

一个产品注册了一个商标,那么它就是单例的

单例模式的类型:
单例模式有两种类型:

懒汉式:在真正需要使用对象时才去创建该单例类对象

饿汉式:在类加载时已经创建好该单例对象,等待被程序使用

单例模式优点:
由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决

单例模式可以在系统设置全局的访问点,优化环共享资源访问,例如可以设计 一个单例类,负责所有数据表的映射处理

常见的七种单例模式:
1、饿汉式:
先创建后使用,线程安全的,浪费内存

public class HungryHanStyle {
   
private static final Logger log = LoggerFactory.getLogger(HungryHanStyle.class);
   
//2.在类的内部创建一个类的实例
   
//类初始化时,立即加载这个对象(没有延时加载的优势)。加载类时,天然的是线程安全的!
   
private static HungryHanStyle hanStyle = new HungryHanStyle();
   
//1.私有化构造方法,使得在类的外部不能调用此方法,限制产生多个对象
   
private HungryHanStyle(){}
   
//3.对外部提供调用方法:将创建的对象返回,只能通过类来调用
  
//方法没有同步,调用效率高!
   
public static HungryHanStyle getInstance(){
       
return hanStyle;
   
}
   
//测试
   
public static void main(String[] args) {
        HungryHanStyle h1 = HungryHanStyle.getInstance()
;
       
HungryHanStyle h2 = HungryHanStyle.getInstance();
       
System.out.println(h2 == h1);
   
}
}

2、懒汉式:
用的时候才创建,线程不安全,加锁会影响效率。资源利用率高。每次调用getInstnce()方法是都要同步,并发效率低

public class SluggardStyle {
   
/** 持有私有的静态实例,防止被引用,此处赋值为null,目的是为延时加载 */
   
private static SluggardStyle sluggardStyle = null;
   
/** 私有的构造方法,防止被实例化 */
   
private SluggardStyle(){}
   
/** 懒汉式,静态工程方法,创建实例 */
   
public static SluggardStyle getInstance(){
       
if(null == sluggardStyle){
           
sluggardStyle = new SluggardStyle();
       
}
       
return sluggardStyle;
   
}
   
//测试
   
public static void main(String[] args) {
        SluggardStyle s1 = SluggardStyle.getInstance()
;
       
SluggardStyle s2 = SluggardStyle.getInstance();
       
System.out.println(s2 == s1);
   
}
}

3、静态内部类方式:
饿汉式与懒汉式的结合,在调优getInstance时创建对象,达到了懒汉式的效果同时是线程安全的

public class StaticStyle {
   
//1.私有化构造方法,使得在类的外部不能调用此方法,限制产生多个对象
   
private StaticStyle(){}
   
//2.在类的内部创建一个类的实例
   
private static class Holder{
       
private static StaticStyle staticStyle = new StaticStyle();
   
}
   
//3.对外部提供调用方法:将创建的对象返回,只能通过类来调用
   
private static StaticStyle getInstance(){
       
return Holder.staticStyle;
   
}
   
//测试
   
public static void main(String[] args) {
        StaticStyle s1 = StaticStyle.getInstance()
;
       
StaticStyle s2 = StaticStyle.getInstance();
       
System.out.println(s2 == s1);
   
}
}

4、枚举方法:

线程安全实现简单,调用效率高,不能延时加载。枚举本身就是单例模式,由JVM从根本上提供保障并且可以天然的防止反射和反序列化的漏洞。需要继承的场景就不适合。

public enum EnumerationStyle {
   
//定义一个枚举的元素,它就代表了Singleton的一个实例。
   
INSTANCE;
   
//对外部提供调用方法:将创建的对象返回,只能通过类来调用
   
public void otherMethod(){
       
//功能处理
   
}
   
//测试
   
public static void main(String[] args) {
        EnumerationStyle e1 = EnumerationStyle.
INSTANCE;
       
EnumerationStyle e2 = EnumerationStyle.INSTANCE;
       
System.out.println(e2 == e1);
   
}
}

5、双重校验锁式:

通常是线程安全,加volatile的作用是禁止指令重排(由于JVM底层内部模型原因,偶尔会出问题,不建议使用)

public class DoubleCheckStyle {
   
//1.私有化构造方法,使得在类的外部不能调用此方法,限制产生多个对象
   
private DoubleCheckStyle(){}
   
//2.在类的内部创建一个类的实例
   
private volatile static DoubleCheckStyle instance; //volatile作用:保证多线程可以正确处理instance
    //3.
对外部提供调用方法:将创建的对象返回,只能通过类来调用
   
private static DoubleCheckStyle getInstance(){
       
if(null == instance){//检查实例,如果为空,就进入同步代码块
           
synchronized (DoubleCheckStyle.class){
               
if (null == instance){//再检查一次,仍未空才创建实例
                   
instance = new DoubleCheckStyle();
               
}
            }
        }
       
return instance;
   
}
}

6、使用ThreadLocal实现

线程安全的,ThreadLocal采用以空间换时间的方式。为每一份线程提供一份变量,因此可以同时访问而互不影响

public class ThreadLocalStyle {
   
//1.私有化构造方法,使得在类的外部不能调用此方法,限制产生多个对象
   
private ThreadLocalStyle(){}
   
//2.在类的内部创建一个类的实例
   
private static final ThreadLocal<ThreadLocalStyle> tls = new ThreadLocal<>() {
       
@Override
       
protected ThreadLocalStyle initialValue(){
           
return new ThreadLocalStyle();
       
}
    }
;
   
//3.对外部提供调用方法:将创建的对象返回,只能通过类来调用
   
public static ThreadLocalStyle getInstance(){
       
return tls.get();
   
}
   
//测试
   
public static void main(String[] args) {
        ThreadLocalStyle t1 = ThreadLocalStyle.getInstance()
;
       
ThreadLocalStyle t2 = ThreadLocalStyle.getInstance();
       
System.out.println(t2 == t1);
   
}
}

7、使用CAS锁实现:
(CAS锁(Compare and Swap):比较并交换,是一种有名的无锁算法,属于乐观锁)。使用CAS算法实现单例模式是线程安全的。

public class CASLocalStyle {
   
//1.私有化构造方法,使得在类的外部不能调用此方法,限制产生多个对象
   
private CASLocalStyle(){}
   
//2.在类的内部创建一个类的实例
   
private static final AtomicReference<CASLocalStyle> instance = new AtomicReference<>();
   
//3.对外部提供调用方法:将创建的对象返回,只能通过类来调用
   
public static final CASLocalStyle getInstance(){
       
for (;;){
            CASLocalStyle current =
instance.get();
            if
(null != current){
               
return current;
           
}
            current =
new CASLocalStyle();
            if
(instance.compareAndSet(null, current)){
               
return current;
           
}
        }
    }
   
//测试
   
public static void main(String[] args) {
        CASLocalStyle c1 = CASLocalStyle.getInstance()
;
       
CASLocalStyle c2 = CASLocalStyle.getInstance();
       
System.out.println(c2 == c1);
   
}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值