JAVA进阶(3)

JAVA设计模式

学习设计模式的好处:学习前辈们的解决办法,有助于代码的编写,让代码设计的更加标准,更加合理。

根据模式是用来完成什么工作来划分,这种方式可分为创建型模式、结构型模式和行为型模式 3 种。
创建型模式 :用于描述“怎样创建对象”,它的主要特点是“将对象的创建与使用分离”。提供了单例、原型、工厂方法、抽象工厂、建造者 5 种创建型模式。
结构型模式 :用于描述如何将类或对象按某种布局组成更大的结构,提供了代理、适配器、桥接、装饰、外观、享元、组合 7 种结构型模式。
行为型模式 :用于描述类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,以及怎样分配职责。提供了模板方法、策略、命令、职责链、状态、观察者、中介者、迭代器、访问者、备忘录、解释器 11 种行为型模式。

常见的设计模式

单例模式

JDK中 Runtime类就是一个单例类,运用饿汉式单例。

在有些系统中,为了节省内存资源、保证数据内容的一致性,对某些类要求只能创建一个实例,这就是所谓的单例模式. 例如,Windows 中只能打开一个任务管理器,这样可以避免因打开多个任务管理器窗口而造成内存资源的浪费,
或出现各个窗口显示内容的不一致等错误。
单例模式有 3 个特点:
1. 单例类只有一个实例对象;
2. 该单例对象必须由单例类自行创建;
3. 单例类对外提供一个访问该单例的全局访问点;  

饿汉式单例(急切式单例)

没有任何线程安全问题

//实现: 把构造方私有化,在单例类中,向外界提供一个方法访问到唯一的单例对象.
public class Window {
    //饿汉式单例,静态的成员在类加载时就会初始化Window对象,由于是静态的所以只有一次
    private static Window window = new Window();
    private Window() {

    }

    public static Window getWindow() {
        return window;
    }
}

如下图可以看到我们创建10个线程调用getWindow方法,获取的对象都是同一个

 

 懒汉式单例

先看一段代码,

public class Window {
    private static Window window;
    private Window() {

    }
//懒汉式单例,在类加载时不创建,在使用时才创建。
    public static Window getWindow() {
        if(window==null){
            window=new Window();
        }
        return window;
    }
}

 这段代码就是懒汉式单例的实现,但是该写法会存在线程安全问题

我们给线程加上休眠,用于测试,结果如下

可以看到在这次我们10个线程所获取的对象有所不同,这就不符合单例的要求,在开发中会出现安全问题,那么我们对以上代码进行改进:

public class Window {
    private static Window window;
    private Window() {

    }
/*
懒汉式单例,在类加载时不创建,在使用时才创建。
 懒汉式单例会出现线程安全问题:
 在多线程访问时,可能会有多个线程同时通过if判断进入创建对象的步骤,从而创建多个对象
 */
    public static synchronized Window getWindow() {
        if(window==null){
            window=new Window();
        }
        return window;
    }
}

我们可以给getWindow方法加上锁,这样就可以解决线程安全问题,通过测试10个线程获取的对象都是同一个,但是加锁之后 每次只能有一个线程访问该方法 效率会很低,我们再对其进行优化。

public class Window {
    private static Window window;
    private Window() {

    }
/*
懒汉式单例,在类加载时不创建,在使用时才创建。
 懒汉式单例会出现线程安全问题:
 在多线程访问时,可能会有多个线程同时通过if判断进入创建对象的步骤,从而创建多个对象
 1.加锁,但效率低
 2.给代码块加锁,双重检索
 */
    public static synchronized Window getWindow() {
        if(window==null){
            synchronized (Window.class){
                if (window==null){
                    window=new Window();
                }
            }
        }
        return window;
    }
}

我们把锁加到代码块上,然后进行第二次判断就可以解决问题了。假设一次性有四个线程同时通过了第一个if判断,但是只能有一个线程可以获取到锁,并进入第二个if判断,通过判断后便可创建一个window对象,而后面三个线程依次获取到锁之后,由于window对象已经被创建,所以会直接执行 return这样既解决了线程安全问题,还增加了效率(可以多个线程同时访问getWindow方法)

工厂模式

工厂模式看名字就知道是 批量生产一类东西,在JAVA中就是批量生产对象;例如spring框架,把创建对象的权利反转给spring框架, 把创建对象和使用对象分离.有一个工厂,专门负责创建对象,使用对象时,只需要找对应的工厂,根据我们产生对象的场景,需求的不同,又分为 简单工厂模式,工厂方法模式,和抽象工厂模式。

简单工厂模式

简单工厂模式并不是 23 种设计模式之一,因为它并不符合开闭原则。主要目的是为了引出工厂方法模式,适合产品子类比较少的、创建操作比较简单的情况
优点
客户端不负责对象的创建,而是由专门的工厂类完成;客户端只负责对象的调用,实现了创建和调用的分离,降低了客户端代码的难度;
缺点
如果增加或减少产品子类,需要修改简单工厂类,违背了开闭原则。如果产品子类过多,会导致工厂类非常的庞大,违反了高内聚原则,不利于后期维护.
适用场景
所有的产品子类都有同一个父类(或接口),属于同一个产品系列产品子类比较少的、创建操作比较简单

工厂方法模式

与简单工厂模式不同,工厂方法模式的对工厂也进行了抽象。有一个抽象的Factory 类(可以是抽象类和接口),这个类将不在负责具体的产品生产,而是只制定一些规范,将实际创建工作推迟到子类去完成

 优点:

客户端不负责对象的创建,而是由专门的工厂类完成;客户端只负责对象的调用,实现了创建和调用的分离,降低了客户端代码的难度;若增加和减少产品子类,不需修改工厂类,只增加产品子类和工厂子类,符合开闭原则即使产品子类过多,不会导致工厂类的庞大,利于后期维护
缺点:
每增加一个产品就要多写一个工厂,需要额外的编写代码,增加了工作量

抽象工厂模式

简单工厂模式:一个工厂类负责同一类所有产品对象的创建,如果产品较多,职责大大增加。增加或减少产品时,需要修改工厂类,违背了开闭原则。
工厂方法模式:一个具体的工厂类负责创建一个单独的产品,如果产品增加或减少,一不需要修改工厂类,符合开闭原则。但是即使这些产品是有联系的,也必须由不同的工厂分别创建.
抽象工厂模式中,一个具体的工厂负责创建一系列相互关联的产品。会简化客户端的调一用。并且更换产品系列非常方便,更换一个工厂类即可。

 如上图,如果需要创建上面四种产品,使用工厂方法模式的话需要创建四个工厂类(奥迪汽车工厂,奥迪手机工厂,宝马汽车工厂,宝马手机工厂),如果使用抽象工厂模式只需要 创建两个工厂类(奥迪工厂和宝马工厂)他们都实现一个抽象工厂接口(该接口里定义两个抽象方法,造手机和造汽车)这样大大减少了工厂类的创建。

原型模式

有时候,我们需要多个实例,但是创建这个实例的过程比较复杂,比如构造函数非常的复杂,执行这个构造函数时会消耗较长的时间,但另外一方面,这个构造函数中的一些信息又没有什么变化(也就是说创建第一个实例时初始化信息是这样的,创建第二个实例时初始化信息还是还是这样的),那么直接使用 new再创建这样一个实例就显得太昂贵了,此时可以使用克隆,也就是复制,就是通过复制现在已经有了的实例来创建新的实例,提高创建速度.
生活案例:同一个简历模板打印了 5 分,要求同一个面试者进行填写,每份简历内容相同。面试者写了第一份后(多么复杂的一个创建对象的过程),还需要再重复的写四份吗?那太费时间了。直接找个复印机复印四份不就可以了。原型模式就是这个道理

代理模式

代理模式就是给一个对象提供一个代理,并由代理对象控制对原对象的引用。它使得客户不能直接与真正的目标对象通信。代理对象是目标对象的代表,其他需要与这个目标对象打交道的操作都是和这个代理对象在交涉。
生活案例:
买房找中介,中介帮助完成找房源、沟通协调、办手续等操作
买二手车找中介,中介负责找车源、做质量检测、过户等,我只要付钱即可购
买火车票不一定要去火车站买,可以通过 12306 网站或者去火车票代售点
代理模式的主要优点有:
1. 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
2. 代理对象可以扩展目标对象的功能;
3. 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度;
代理模式结构
1. 抽象主题(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。
2. 真实主题(Real Subject)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
3. 代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。其结构图如图所示。
代理实现可以分为静态代理和动态代理。

静态代理

静态代理模式的特点,代理类接受一个 Subject 接口的对象,任何实现该接口的对象,都可以通过代理类进行代理,增加了通用性。
优点:可以做到在符合开闭原则的情况下对目标对象进行功能扩展。
缺点:一个代理类只能代理一个接口,工作量太大;代理类是运行前编码已经完成的;必须先有接口,再有代理;接口一旦发生变量,代理类也要修改,违反开闭原则

动态代理

在动态代理中我们不再需要再手动的创建代理类,我们只需要编写一个动态处理器就可以了。真正的代理对象在运行时为我们动态的来创建。
动态代理分为 jdk 动态代理和 cglib 动态代理
jdk动态代理:
动态代理是实现方式,是通过反射来实现的,借助 Java 自带的
java.lang.reflect.Proxy,通过固定的规则生成。
其步骤如下:
1. 编写一个委托类的接口,即静态代理的
2. 实现一个真正的委托类,即静态代理的
3. 创建一个动态代理类,实现 InvocationHandler 接口,并重写该 invoke 方法
4. 在测试类中,生成动态代理的对象。
jdk 动态代理总结:
虽然相对于静态代理,动态代理大大减少了我们的开发任务,同时减少了对业务接口的依赖,降低了耦合度。但是他仅支持interface的代理。
cglib代理
JDK 实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,如何实现动态代理呢,这就需要 CGLib 了。
CGLIB(Code Generator Library)是一个强大的、高性能的代码生成库。CGLib 采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。CGLIB 相比于 JDK 动态代理更加强大。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值