day23 【设计模式-单列设计模式,多列设计模式,工厂设计模式,动态代理,Lombok】

第一章 单例设计模式

1.1单例设计模式的概述

正常情况下一个类可以创建多个对象

public class Person(){
    // 默认生成一个公共的空参构造方法
}
// 测试类
public static void main(String[] args) {
	// 正常情况下一个类可以创建多个对象
	Person p1 = new Person();
	Person p2 = new Person();
	Person p3 = new Person();
    // ...
}

但是有些时候的某些类, 我们希望只能创建单一的一个对象, 这时候我们需要使用到单例设计模式, 下面我们来介绍一下单例设计模式.

单例设计模式的作用

单例模式,是一种常用的软件设计模式。通过单例模式可以保证系统中,应用该模式的这个类只有一个实例。即一个类只有一个对象实例。

单例设计模式实现步骤

  1. 将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
  2. 在该类内部产生一个唯一的实例化对象
  3. 定义一个静态方法返回这个唯一对象。

例设计模式的类型

根据实例化对象的时机单例设计模式又分为以下两种:

  1. 饿汉单例设计模式
  2. 懒汉单例设计模式

1.2饿汉式单例设计模式

饿汉单例设计模式就是使用类的时候已经将对象创建完毕,不管以后会不会使用到该实例化对象,先创建了再说。很着急的样子,故被称为“饿汉模式”。

代码如下:

public class Person {
    // 使用饿汉式单例设计模式: 比较着急,不管要不要获取我这个类的对象,先创建了该对象再说
    // 1. 将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
    private Person(){
    }

    // 2. 在该类内部产生一个唯一的实例化对象
    private static final Person p = new Person();

    // 3. 定义一个静态方法返回这个唯一对象。
    public static Person getInstance(){
        return p;
    }

    // ....
}

public class Test {
    public static void main(String[] args) {
        Person p1 = Person.getInstance();
        Person p2 = Person.getInstance();
        Person p3 = Person.getInstance();

        System.out.println(p1);
        System.out.println(p2);
        System.out.println(p3);

    }
}

1.3懒汉式单例设计模式

懒汉单例设计模式就是调用getInstance()方法时实例才被创建,先不急着实例化出对象,等要用的时候才实例化出对象。不着急,故称为“懒汉模式”。

代码如下:

public class Person {
    // 懒汉式单例: 不着急,只要当你调用了getInstance静态方法获取对象的时候,就创建,其他时候不创建
    // 1. 将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
    private Person(){

    }

    // 2. 在该类内部产生一个唯一的实例化对象
    private static Person p ;// 默认值为null

    // 3. 定义一个静态方法返回这个唯一对象。
    public static synchronized Person getInstance(){
        // 创建Person类的唯一对象
        // 判断一下,如果p这个成语变量的值为null,就创建,不为null,说明该对象已经创建了,直接返回即可
        if (p == null){
            p = new Person();
        }
        return p;
    }

    // ...
}

public class Test {
    public static void main(String[] args) {
        Person p1 = Person.getInstance();
        Person p2 = Person.getInstance();
        Person p3 = Person.getInstance();

        System.out.println(p1);
        System.out.println(p2);
        System.out.println(p3);


    }
}

注意:懒汉单例设计模式在多线程环境下可能会实例化出多个对象,不能保证单例的状态,所以加上关键字:synchronized,保证其同步安全。

小结

单例模式可以保证系统中一个类只有一个对象实例。

实现单例模式的步骤:

  1. 将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
  2. 在该类内部产生一个唯一的实例化对象,并且将其封装为private static类型的成员变量。
  3. 定义一个静态方法返回这个唯一对象。

第二章 多例设计模式

多例设计模式的作用

多例模式,是一种常用的软件设计模式。通过多例模式可以保证系统中,应用该模式的类有固定数量的实例。多例类要自我创建并管理自己的实例,还要向外界提供获取本类实例的方法。

实现步骤

​ 1.创建一个类, 将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。

​ 2.在该类内部产生固定数量的实例化对象 ----> 集合

​ 3.提高一个静态方法来随机获取一个该了的实例化对象

实现代码

public class Person {
    // 使用多例设计模式: 保证程序运行期间该类只有固定数量的对象产生
    // 1.创建一个类,  将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
    private Person(){
    }

    // 2.在该类内部产生固定数量的实例化对象 ----> 集合  只能产生依次固定数量的对象

    // 2.1 定义一个存放该类对象的集合
    private static ArrayList<Person> list = new ArrayList<>();

    // 2.2 在静态代码块中,创建固定数量的对象,添加到集合中
    static {
        // 创建固定数量的该类对象
        for (int i = 0; i < 3; i++) {
            Person p = new Person();
            list.add(p);
        }
    }

    // 3.提高一个静态方法来随机获取一个该了的实例化对象
    public static Person getInstance(){
        // 创建一个Random对象
        Random r = new Random();
        // 随机产生一个list集合的索引
        int index = r.nextInt(list.size());// [0,3) 0,1,2
        // 根据索引获取对象
        Person p = list.get(index);
        // 返回对象
        return p;
    }
}

测试结果

public class Test {
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            Person p1 = Person.getInstance();
            System.out.println(p1);
        }

    }
}

在这里插入图片描述
小结

多例模式可以保证系统中一个类有固定个数的实例, 在实现需求的基础上, 能够提高实例的复用性.

实现多例模式的步骤:

  1. 创建一个类, 将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
  2. 在类中定义该类被创建的总数量
  3. 在类中定义存放类实例的list集合
  4. 在类中提供静态代码块,在静态代码块中创建类的实例
  5. 提供获取类实例的静态方法

第三章 工厂设计模式

工厂模式的介绍

​ 工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。之前我们创建类对象时, 都是使用new 对象的形式创建, 除new 对象方式以外, 工厂模式也可以创建对象.

耦合度: 类与类之间的关系,如果关系比较强,高耦合, 如果关系比较弱,低耦合

10个类:

测试类: 创建这10个类的对象

以前: 直接通过new 来创建 测试类和10个类关系起来

工厂模式: 定义一个类,专门用来创建这10个类的对象, 并提供获取的方法,那这个时候测试类只需要跟测试类关联 低耦合

工厂模式的作用

将前端代码与要创建的对象分开,前端不需要直接创建对象,也就不需要关心创建对象时需要的数据。只需要通过工厂获取对象即可。

  • 解决类与类之间的耦合问题

案例演示

需求

  1. 编写一个Car接口, 提供run方法
  2. 编写一个Falali类实现Car接口,重写run方法
  3. 编写一个Benchi类实现Car接口,重写run方法

提供一个工厂类,可以用来生产汽车对象

实现代码

1.编写一个Car接口, 提供run方法

public interface Car {
    public void run();
}

2.编写一个Falali类实现Car接口,重写run方法

public class Falali implements Car {
    @Override
    public void run() {
        System.out.println("法拉利以每小时500公里的速度在奔跑.....");
    }
}

3.编写一个Benchi类实现Car接口

public class Benchi implements Car {
    @Override
    public void run() {
        System.out.println("奔驰汽车以每秒1米的速度在挪动.....");
    }
}

4.提供一个CarFactory(汽车工厂),用于生产汽车对象

public class CarFactory {
    /**
     * @param id : 车的标识
     *           benchi : 代表需要创建Benchi类对象
     *           falali : 代表需要创建Falali类对象
     *           如果传入的车标识不正确,代表当前工厂生成不了当前车对象,则返回null
     * @return
     */
    public Car createCar(String id){
        if("falali".equals(id)){
            return new Falali();
        }else if("benchi".equals(id)){
            return new Benchi();
        }
        return null;
    }
}

5.定义CarFactoryTest测试汽车工厂

public class CarFactoryTest {
    public static void main(String[] args) {
        CarFactory carFactory = new CarFactory();
        Car benchi = carFactory.createCar("benchi");
        benchi.run();
        Car falali = carFactory.createCar("falali");
        falali.run();
    }
}
  • 工厂模式的存在可以改变创建类的对象的方式,解决类与类之间的耦合.

第四章 Lombok【自学扩展】

lombok介绍

  • lombok可以使用注解的方式让一些代码变的简洁 方便
  • 实体类中有一些固定的代码:构造方法,getter/setter、equals、hashcode、toString方法都是固定的,写出来看着比较麻烦。而Lombok能通过注解的方式,在编译时自动为属性生成这些代码。

lombok使用

​ 1. 添加lombox的jar包:

​ 将lombok.jar(本例使用版本:1.18.10),添加到模块目录下,并添加到ClassPath在这里插入图片描述
2. 为IDEA添加lombok插件(连接网络使用)在这里插入图片描述
第二步:
在这里插入图片描述
第三步:

在这里插入图片描述

第四步:
在这里插入图片描述
4. 安装完毕后,重启IDEA。

  1. 新建一个类:Student在这里插入图片描述
    lombok常用注解
  • @Getter和@Setter

    • 作用:生成成员变量的get和set方法。
    • 写在成员变量上,指对当前成员变量有效。
    • 写在类上,对所有成员变量有效。
    • 注意:静态成员变量无效。
  • @ToString:

    • 作用:生成toString()方法。
    • 该注解只能写在类上。
  • @NoArgsConstructor和@AllArgsConstructor

    • @NoArgsConstructor:无参数构造方法。
    • @AllArgsConstructor:满参数构造方法。
    • 注解只能写在类上。
  • @EqualsAndHashCode

    • 作用:生成hashCode()和equals()方法。
    • 注解只能写在类上。
  • @Data

    • 作用: 生成setter/getter、equals、canEqual、hashCode、toString方法,如为final属性,则不会为该属性生成setter方法。

    • 注解只能写在类上。

第五章 动态代理

代理模式概述

为什么要有“代理”?生活中就有很多代理的例子,例如,我现在需要出国,但是我不愿意自己去办签证、预定机票和酒店(觉得麻烦 ,那么就可以找旅行社去帮我办,这时候旅行社就是代理,而我自己就是被代理了。

代理模式的定义:被代理者没有能力或者不愿意去完成某件事情,那么就需要找个人代替自己去完成这件事,这个人就是代理者, 所以代理模式包含了3个角色: 被代理角色 代理角色 抽象角色(协议)

静态代理:

public interface Happy {// 协议,被代理者需要代理的方法,就定义在这里,然后让代理者和被代理者去实现
    // 被代理者实现: 为了确保和代理者实现的方法一致
    // 代理者实现: 为了增强被代理者的这些方法
    public abstract void happy();
}


public class JinLian implements Happy {

    public void happy(){
        System.out.println("金莲在happy...");
    }

}


public class WangPo implements Happy{
    // 成员变量
    JinLian jl;

    // 构造方法
    public WangPo(JinLian jl) {
        this.jl = jl;
    }

    // 成员方法
    @Override
    public void happy() {
        System.out.println("王婆以做衣服的名义开好房间,并把2人约到房间里...");
        // 金莲happy
        jl.happy();
        System.out.println("王婆打扫战场...");
    }
}


public class XiMen {
    public static void main(String[] args) {
        /*
            案例: 金莲要找西门happy
            代理模式的定义:被代理者没有能力或者不愿意去完成某件事情,那么就需要找个人代替自己去完成这件事,这个人就是代理者,
            所以代理模式包含了3个角色: 被代理角色     代理角色    抽象角色(协议)

         */
        // 不请代理: 金莲直接找西门happy
        // 创建金莲对象
        JinLian jl = new JinLian();
        // happy
        // jl.happy();

        // 请代理: 静态代理,代理类真实存在
        Happy wp = new WangPo(jl);// wp:代理对象   WangPo类: 代理类   Happy接口: 协议  JinLian: 被代理类
        wp.happy();
    }
}

动态代理介绍

  • 概述 : 动态代理就是直接通过反射生成一个代理对象,代理对象所属的类是不需要存在的

  • 动态代理的获取:

    ​ jdk提供一个Proxy类可以直接给实现接口类的对象直接生成代理对象

动态代理相关api介绍

Java.lang.reflect.Proxy类可以直接生成一个代理对象

  • Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)生成一个代理对象
    • 参数1:ClassLoader loader 被代理对象的类加载器
    • 参数2:Class<?>[] interfaces 被代理对象的要实现的接口
    • 参数3:InvocationHandler h (接口)执行处理类
    • 返回值: 代理对象
    • 前2个参数是为了帮助在jvm内部生成被代理对象的代理对象,第3个参数,用来监听代理对象调用方法,帮助我们调用方法
  • InvocationHandler中的Object invoke(Object proxy, Method method, Object[] args)方法:调用代理类的任何方法,此方法都会执行
    • 参数1:代理对象(慎用)
    • 参数2:当前执行的方法
    • 参数3:当前执行的方法运行时传递过来的参数
    • 返回值:当前方法执行的返回值

案例演示
在这里插入图片描述

案例1: 代理方法无参数

public interface Happy {// 协议,被代理者需要代理的方法,就定义在这里,然后让代理者和被代理者去实现
    // 被代理者实现: 为了确保和代理者实现的方法一致
    // 代理者实现: 为了增强被代理者的这些方法
    public abstract void happy();
}


public class JinLian implements Happy {

    public void happy(){
        System.out.println("金莲在happy...");
    }

}


public class XiMen {
    public static void main(String[] args) {
        /*
            案例: 金莲要找西门happy
            代理模式的定义:被代理者没有能力或者不愿意去完成某件事情,那么就需要找个人代替自己去完成这件事,这个人就是代理者,
            所以代理模式包含了3个角色: 被代理角色     代理角色    抽象角色(协议)

         */
        // 不请代理: 金莲直接找西门happy
        // 创建金莲对象
        JinLian jl = new JinLian();
        // happy
        // jl.happy();

        // 请代理: 静态代理,代理类真实存在
        Happy wp = new WangPo(jl);// wp:代理对象   WangPo类: 代理类   Happy接口: 协议  JinLian: 被代理类
        wp.happy();

        /*
            问题:
                1.金莲不方便直接找西门happy
                2.金莲的happy方法需要增强一下,例如:happy之前需要开房,happy之后需要打扫战场

            静态代理: 代理类是真实存在的,通过代理类产生代理对象
            动态代理: 代理类是不真实存在的,在程序运行中,直接产生代理对象
            前提: 被代理类需要实现接口
            动态代理实现获取代理对象:
                jdk提供一个Proxy类可以直接给实现接口类的对象直接生成代理对象
                Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)生成一个代理对象
                    参数1loader:被代理类的类加载器
                    参数2interfaces: 被代理类所有实现的接口的Class对象
                    参数3InvocationHandler: 执行处理类
                    前2个参数是为了帮助在jvm内部生成被代理类的代理对象,第3个参数用来监听代理对象调用的方法,帮助我们代理对象调用方法
         */
        System.out.println("=========================================");

        // 使用动态代理直接产生金莲的代理对象
        // 动态代理: 代理类是不真实存在的,但代理类是一定实现了被代理类的接口的
        // p:动态代理产生的代理对象 代理类是不真实存在的
        Happy p = (Happy) Proxy.newProxyInstance(JinLian.class.getClassLoader(), JinLian.class.getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                // 回调方法: 当代理对象调用了方法,就会来执行该invoke方法, 在该方法中就可以增强被代理类的方法
                // 参数1: 生成的代理对象 这里就是p这个代理对象 (慎用)
                // 参数2: 当前代理对象执行的方法 这里method就是happy()方法对象
                // 参数3: 当前代理对象执行的方法,传入的实际参数
                // 返回值:当前代理对象执行的方法的返回值
                // System.out.println("invoke");
                // 王婆开房
                System.out.println("王婆以做衣服的名义开好房间,把2人约到房间...");

   				// 金莲happy
                method.invoke(jl);
             

                // 王婆打扫
                System.out.println("王婆打印战场...");
                return null;
            }
        });

        // 代理happy
        p.happy();// 无参数
    }
}

案例2: 代理方法有参数和代理多个方法

public interface Happy {// 协议,被代理者需要代理的方法,就定义在这里,然后让代理者和被代理者去实现
    // 被代理者实现: 为了确保和代理者实现的方法一致
    // 代理者实现: 为了增强被代理者的这些方法
    public abstract void happy();

    public abstract void sleep(int time);
}

public class JinLian implements Happy {

    public void happy(){

        System.out.println("金莲在happy...");

    }

    public void sleep(int time){// 单位分钟
        System.out.println("金莲在睡觉...time:"+time);
    }

}


public class XiMen {
    public static void main(String[] args) {
        /*
            问题:
                1.金莲不方便直接找西门happy
                2.金莲的happy方法需要增强一下,例如:happy之前需要开房,happy之后需要打扫战场

            静态代理: 代理类是真实存在的,通过代理类产生代理对象
            动态代理: 代理类是不真实存在的,在程序运行中,直接产生代理对象
            前提: 被代理类需要实现接口
            动态代理实现获取代理对象:
                jdk提供一个Proxy类可以直接给实现接口类的对象直接生成代理对象
                Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)生成一个代理对象
                    参数1loader:被代理类的类加载器
                    参数2interfaces: 被代理类所有实现的接口的Class对象
                    参数3InvocationHandler: 执行处理类
                    前2个参数是为了帮助在jvm内部生成被代理类的代理对象,第3个参数用来监听代理对象调用的方法,帮助我们代理对象调用方法
         */
        System.out.println("=========================================");

        // 使用动态代理直接产生金莲的代理对象
        // 动态代理: 代理类是不真实存在的,但代理类是一定实现了被代理类的接口的
        // p:动态代理产生的代理对象 代理类是不真实存在的
        Happy p = (Happy) Proxy.newProxyInstance(JinLian.class.getClassLoader(), JinLian.class.getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                // 回调方法: 当代理对象调用了方法,就会来执行该invoke方法, 在该方法中就可以增强被代理类的方法
                // 参数1: 生成的代理对象 这里就是p这个代理对象 (慎用)
                // 参数2: 当前代理对象执行的方法 这里method就是happy()方法对象
                // 参数3: 当前代理对象执行的方法,传入的实际参数
                // 返回值:当前代理对象执行的方法的返回值
                // System.out.println("invoke");
                // 王婆开房
                System.out.println("王婆以做衣服的名义开好房间,把2人约到房间...");
				
                // 访问被代理对象的方法的方式:
                 // 方式一:
                // 如果代理对象调用的方法没有参数,那么args等于null
                /*if (args == null) {
                    // 金莲happy
                    method.invoke(jl);
                } else if (args.length == 1) {

                    // 金莲sleep
                    method.invoke(jl,args[0]);
                }*/

                // 方式二:
                // method.invoke(jl,args);

                // 方式三:
                JinLian jl = JinLian.class.newInstance();
                method.invoke(jl,args);


                // 王婆打扫
                System.out.println("王婆打印战场...");
                return null;
            }
        });

        // 代理happy
        p.happy();// 无参数
        System.out.println("=========================");
        p.sleep(10);// 有参数

    }
}

总结- 能够说出单例设计模式的好处

保证使用这种模式创建的类,在程序运行期间只会创建一个该类的对象

  • 能够说出多例模式的好处
    保证使用这种模式创建的类,在程序运行期间只会创建固定数量的该类对象

  • 能够说出动态代理模式的作用
    1.在程序运行期间,动态产生一个代理对象,代理类不会真实存在
    2.代理对象可以帮助被代理对象完成功能(增强被代理对象的功能)

  • 能够使用Proxy的方法生成代理对象
    Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)生成一个代理对象
    参数1loader:被代理类的类加载器
    参数2interfaces: 被代理类所有实现的接口的Class对象
    参数3InvocationHandler: 执行处理类
    前2个参数是为了帮助在jvm内部生成被代理类的代理对象,第3个参数用来监听代理对象调用的方法,帮助我们代理对象调用方法

    public Object invoke(Object proxy, Method method, Object[] args);
    // 回调方法: 当代理对象调用了方法,就会来执行该invoke方法, 在该方法中就可以增强被代理类的方法
    // 参数1: 生成的代理对象 这里就是p这个代理对象 (慎用)
    // 参数2: 当前代理对象执行的方法 这里method就是happy()方法对象
    // 参数3: 当前代理对象执行的方法,传入的实际参数
    // 返回值:当前代理对象执行的方法的返回值

  • 能够使用工厂模式编写java程序
    提供一个工厂类,在工厂类中提供一个静态方法,用来获取对象

练习模块

对Collection接口进行代理,以前的remove(Object obj)方法是删除集合中第一次出现的元素(比如集合中有多个“abc”,调用remove(“abc”)后只会删除一个元素)。代理后,要求在调用remove(Object obj)方法后,能够删除集合中所有匹配的元素。【动态代理】

Collection<String> list = new ArrayList<>();
        list.add("aaa");
        list.add("aaa");
        list.add("aaa");
        list.add("bbb");
        list.remove("aaa");
        System.out.println(list);
     Collection<String>pox=(Collection<String>)Proxy.newProxyInstance(list.getClass().getClassLoader(), list.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
                if (method.getName().equals("remove")){
                    Iterator<String> iterator = list.iterator();
                    while (iterator.hasNext()) {
                        String next = iterator.next();
                        if (objects[0].equals(next)){
                            iterator.remove();
                        }
                    }
                }return  method.invoke(list,objects);
            }
        });

pox.remove("aaa");
        System.out.println(list);
    }}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值